Skip to main content

Adventures in JScript: Episode Seven - Attached Behaviors, Part 2

Introduction:

Last time I scratched out a frenzied look at the basics of Attached Behaviors. Based upon a couple of comments, I'm going to dive into them a little deeper and spend a little time on the actual code used in my now infamous a_datebox.htc. If you'd like to see the requirements for this little monster, take a gander at Episode 6 in "Our Requirements". Lest anyone think that I'm a card-carrying expert on all things Behavior, I need to provide the following disclaimer: Most of this "knowledge" was acquired by reading "Windows Web Scripting Developer's Guide" by Dan Heflin & Todd Ney. I've mentioned it before and I'll mention it again, it's about the only book I've found on Behaviors and these guys did a commendable job describing the technology. It's out of print, but I picked it up on eBay for like $5....


As I've mentioned a couple of times, Behaviors are nothing new in the JScript world. Microsoft initially introduced them in 1998/1999 to the public and they were overhauled in 2001 (I think that's when it was...memory is fading..). So they've been around a while. So if they're so cool, why haven't we heard more about them? To address that specific question, I have to guess. My guess is split into two sides of the answer.


One, they are predominantly client side scripting solutions. The last 5 or so years have been big ones in the web dev world, but primarily (my opinion here) focussed on server side technologies. And it makes sense, Microsoft really, really wants to sell Windows. The ASP group has figured out a lot of neat ways to accomplish that and that's kind of the new model on how to sell more Windows. The IE guys with Behaviors were stuck in the old model which was to get people to use IE. Behaviors at their finest only require IE, they really don't care what web server they're shoved down by. So the Behavior technology was some what ignored by Microsoft marketing because it served the old model and not the new model. That's purely my opinion, but it seems to make some sense. My comment on that is that doesn't IE (and specifically require Behaviors) require Windows too? Just a question.....


The second reason they are largely ignored (IMHO) is that they are exclusively an IE for Windows feature. All those standards compliant Mozilla/FireFox loving folks stereotypically want nothing to do with a feature that is exclusively Microsoft. The Mac folks out there scream foul. And for good reason: if you're building a public facing web site and your functionality excludes a specific user base that MIGHT be a bad thing. But a lot of us develop for intranet/internal use, where the browser choice is corporately mandated and in most cases, it's IE Windows. Others of us write fairly sophisticated web-based applications and we are willing to mandate the browser, just like any software vendor mandates its standard systems requirements. If I buy some software that clearly says Windows only on the side of the box, and I'm a Mac user, I shouldn't be too surprised that the software doesn't work...

The Nasty Underbelly

So how does a Behavior work inside the browser? Leaving my standard answer aside (it's magic, who cares?), I'll briefly explain. The IE engine, MSHTML.DLL, does not talk directly to an HTC. Instead, a proxy DLL called SCROBJ.DLL serves as an interface between the HTC and the engine. The proxy DLL uses interface handlers to talk to the HTC. These interface handlers are used to simplify the COM interfaces exposed to the Web Page. An HTC is just a wrapper that makes calls to the interface handler. Here's a doodle to roughly show how this works:

Or you could take my word for it and go with the "it's magic, who cares?" answer.... So let's take a look at the basics of the Attached Behavior and that starts with the overall layout.

The Anatomy of an Attached Behavior

In my previous article I stated that an Attached Behavior is just a file, mostly script, saved with a .htc extension. What makes the .htc file different from the standard .js include file are the HTC-specific elements (I stick them at the top) that describe the properties, methods and events that define the behavior's functionality. These elements have to be valid XML and they are actually parsed by IE to create the Behavior's public interface to its containing page. On the following lovely table is a list of these elements. All of the elements have a Name and ID attribute (okay, not the Attach, it's ID only). The Name attribute is used by the containing/consuming page to refer to the specific element and the ID attribute is used internally by the Behavior to identify that same element. It'll all make sense in a bit....


Element Purpose Example
Component Used in the HTC file to define it as a component. <public:component name="a_MyNameHere" id="" urn="">
Property A public attribute of the component. <public:property name="Property1" id="" get="GetProperty1" put="PutProperty1" persist="" value= "" />
Method A public procedure of the component that is exposed to the component's container <public:method name="DoStuff" id=""/>
Attach Binds an event from the component's container to the component <public:attach event="onBlur" for="element" handler="MyOnBlur" id=""/>
Event Used by the HTC to create an event that is exposed to it's container <public:event name="onSomething" id="Evt1" />

In places where I've left some of these element attributes blank, id="" for instance, you can leave them off entirely if you aren't using them. While there are some rules as to how these are put together, these are still very flexible and in the true spirit of JScript, things will just muddle through and guess what you as the developer meant. In last week's episode, I described these elements as the Interface Declaration (my term) but that's exactly what they are. Now let's examine each of the elements in a little detail:

The Component Element

The Component Element is the single required element for a Behavior. Makes sense too, since this is how the consumer identifies the behavior and all... Seriously, the Component Element is used to identify the contents of the file as an HTC and without it, I don't think these things work. I did an okay job of describing this last week, so I'll highlight. The Component Element can have 3 attributes, but interestingly, none are required. They help uniquely identify this Behavior to the consuming page, and that's probably a good thing. As a component developer, we need to at least imagine a case where the consumer is using multiple components and may need to uniquely identify this one from another one...that's just good design. I personally have only ever used the Name and ID attributes in my creations. The other requirement on the Component Element is that all other Elements have to be contained between the open and close tags. As I said last week, some people include their <script> block inside the Component Element (like me) and some don't. It's purely a matter of style as far as I'm concerned. Here's the Component Element from our example: <public:component name="a_datebox" urn="">. While not very exciting, it is very typical.

The Property Element

The Property Element is one of the coolest things about Behaviors IMHO. Many of us are very used to setting properties on objects, especially in JScript where Expandos make SomeObject.SomeProperty = "Hello"; trivial. What the Property Element of a Behavior does for us is to give the object (Behavior) designer more finer grained control over the use of the object. Need the property to be read only? No problem. Need it to have a default value? How about performing some action in the "setter" ? No problem, no problem. In traditional JScript development, doing these things is a little more difficult, in Behaviors, it's a piece of cake. In the example above, I've shown most of the attributes that the Property Element can have, but it only has one required attribute, the Name attribute. (Now why you'd have a property without anything else is your business.) You can have an ID, and you can have an InternalName attribute. I don't use InternalName but the docs say that if the InternalName attribute is set, it should be used inside the Behavior to refer to the property; otherwise the ID is used for that purpose. The next bunch of attributes are where this property stuff gets serious. The get and put properties should be very familiar to anyone who's coded in (allegedly) more powerful languages. I call them "getter" and "setter" and other people have names for them as well. Both attributes describe functions (that has to be included in the Behavior's <script> block) that are executed when the consuming page requests the properties value (the get) or sets the properties value (the put). If you want the property to be read-only, omit the put attribute. It's that simple. Since these attributes ultimately describe a JScript function, you can do things in these functions, like data validation and initialization that you simply can't in more simpler object/property schemes. If we move on to the last two attributes, persist and value, we get even more power.


The value attribute is very simple. The value of the attribute is the default value of the property. Said another way, if the consumer requests the property's value before it has set the property's value, the Behavior will "send" this value (the default or value="whatever") to the consumer. The persist attribute is a boolean only attribute (persist="true" or persist="false"). If true, the property's value is persisted with the consuming page.


In our datebox example, we have a number of properties. Let's look at them:


Property Purpose Read Only? Default Value?
xvalue Allows direct access to the numeric date value.
yvalue Sets/gets the date in the mm/dd/yyyy format.
text Allows direct access to the text date value. Y
error Allows direct access to the control's error state. Y
longmonth By setting this true, the month will be represented in long form, January rather than Jan. False
monthnumber By setting this true, the month will be represented in numeric form, 01 rather than Jan. False
errortext Allows the client to retrieve the error text of this control Y

Why the xvalue and yvalue? Well it's like this: on the consuming page if we access the .value of the control that the Behavior is attached to, txtWhatever.value for example, the .value is equal to whatever is visually present in the control. Since this wacky Behavior changes its display on Blur and on Focus, the .value is not consistent. So I came up with the xvalue and yvalue properties (if you 've known me for a while, you'll remember really where this idea came from, right Dave?). If you access txtWhatever.xvalue, you will always get "012004", whether the control has focus or not. Along the same lines, txtWhatever.yvalue will return "01/01/2004". This was added since the database guys want to save this as a date and not some crazy string and nobody but me could figure out how to make this conversion! The txtWhatever.text property always returns the "text" version of the date and the txtWhatever.longmonth and txtWhatever.monthnumber properties control this "text". By default, the "text" version will return "Jan 2004" if "012004" is entered. If the .longmonth property is set to true, the "text" will be "January 2004". If the .monthnumber is set to true, the "text" will be "01/2004". These two properties and resulting features were not in the original requirement and in truth, are not even used. But they were absolutely effortless to add and make the control potentially more usable by other consumers. The hard core developer world might scoff on this because this is scope creep instigated by a developer, increasing the test matrix, but I say, if it makes it better and adds no time to the schedule, go for it! Here is the complete set of Property Elements for the a_datebox.htc:


<public:property name="xvalue" get="GetValue" put="PutValue" />
<public:property name="yvalue" get="GetYValue" put="PutYValue" />
<public:property name="text" get="GetText" />
<public:property name="error" get="GetError" />
<public:property name="longmonth" get="GetLongMonth"
put="PutLongMonth" value="false" />
<public:property name="monthnumber" get="GetMonthNumber"
put="PutMonthNumber" value="false" />
<public:property name="errortext" get="GetErrorText" />

As you can see, we have a number of get and put functions in this, let's look a couple of representative ones, GetYValue and PutYValue. First, function GetYValue():


function GetYValue()
{
if(!mError)
{
if(mValue!="")
{
return MakeDateSlashes(mValue);
}
else
{
return "";
}
}
else
{
return "Error";
}
}

The purpose of GetYValue() is to return the YValue back through the Property Element to the consuming page in our desired format. The first thing we look at is if we are in an error condition, which is held in a module level variable, mError. Module level variables (my naming convention starts them with "m") are really private variables that are used only by the Behavior code itself. They are not publicly exposed. This variable mError defaults to false, since when the control is blank, we don't have an error. When data is entered into the control, those functions validate the entry and if it is bad, the value of mError is driven true until the error condition is remedied. But since we are just a component, we don't know exactly when that'll be, so we must protect other exits from this condition. Therefore, if we have junk in the control, and we are in error-land, the control shouldn't return a yvalue, now should it? So if we are in error, the control returns the string "Error" to the consuming page. On that page, the accessor function will need to test for "Error" and do something appropriate. But if we're not in error, we move to the next test, which is if mValue is not blank.


mValue is another module level variable (some people call them local variables) that holds the actual input value, like "012004". If it has a value and we're not in error, it's a good value. If it doesn't have a value and we're not in error, we can logically deduce that the control is blank, thus the return ""; line. But if we don't do that, we send mValue to MakeDateSlashes(), a private function that does basic string manipulation and turns "012004" into "01/01/2004". We know it's private since it's not declared in the Interface Declaration. Private functions are not visible or executable to the consuming page.


Let's look at PutYValue:


function PutYValue(vValue)
{
if(GoodDateSlashes(vValue))
{
mValue = RemoveSlashes(vValue);
element.value = mText;
}
else
{
mError = true;
}
}

The job here is to set our internal value, mValue with a valid value assuming that what is being sent is valid. First all Put functions have some value that is being "sent" in. I choose to use vValue but anything can be done. Now remember the purpose of PutYValue: it receives as string, ideally looking like "01/01/2004" and hopefully fills vValue with "012004". So the first thing we do is call another private function, GoodDateSlashes() which tests to see if vValue has a good date. If not, we are heading straight into error-land and set mError = true;. Notice we don't return anything with a Put. This is proper form for properties. If the data is good, we set mValue with a value that is returned by RemoveSlashes, another private function that removes the slashes (thus the name!). The next line is an interesting one. What this does is sets the value of the visual control on the consuming page using a local variable mText. The keyword element is special to Behaviors and gives us access to the object that the Behavior is attached to. We can then affect the object in standard JScript ways from inside the Behavior. mText is set to the appropriate visual "text" (based upon all the formatting properties) down in RemoveSlashes. The two variables, mValue and mText always contain our current "text" and then depending what is going on, we can access these in a number of ways. The rest of the Property functions are very similar to these two, take a look at the code if you'd like. Let's move on to Methods.

The Method Element

Compared to the Property Element, this one is a breeze. Real simple: the Name attribute is required and here too, you can have the InternalName attribute. That's about it. Oh yeah, you need to have a Method (actually a function) in the <script> block that corresponds to the Name attribute...they have to match. And simply, these are JScript functions that you want executed from the consumer page. In the sample Behavior, I only have two public methods, one, InsertDate is actually a leftover of a test idea and not really used. The other, ClearOut was designed to completely wipe out both the element.value and all the internal module level values. I found out the hard way that Behaviors seem to cache differently than standard web controls and if you really, really want to wipe them clean, do something like ClearOut.


Method Purpose
ClearOut Completely eliminates all the values buried in the HTC. These dudes have a nasty habit of caching data, so use this function before setting a value/xvalue/yvalue
InsertDate A public method that allows you to set the YValue if for some reason you don't like the property set

And here's how it looks in the Interface Declaration:


<public:method name="ClearOut" /> 
<public:method name="InsertDate" />

Since I don't have much else to say about the Method Element, I thought I'd take a second to discuss the use of Behaviors in Server based technologies, like ASP, ASP.Net, even PHP, in specific, when you use the traditional Form/POST style of data access. I'm going to assume that this means something to you...if not, study up a little before we move on. Basically, since Form/POST sweeps the .value of every <form> element and submits them via the Request object, we have a bit of a quandary. This control will work fine, if you are willing to have your server page test the value, since it is possible that you'll have either the .text value or the .xvalue of the control up there. And forget about the .yvalue! I've seen some people use these, then duplicate the code up on the server for all the data manipulation. Unfortunately, the only other solution is an old school HTML hack, and that's to use a hidden field on your consuming page and always make certain that the hidden field has the "value" that you want. Then on SUBMIT, that value will be in the REQUEST stream.

The Finish Line:

OK, there was a little more depth here, hope that works for everyone (you know who are!). Next time, I'd like to hit events, which manifest themselves in three ways: the Attach Element, the Event Element and the HTC's intrinsic events. Events are very cool in Behaviors, hopefully in Episode 6 you got a taste for it, and we'll look at them and more in the continuation of this next time (hopefully next week....). To repeat again, to see the datebox control in action, take a look at this. It's a test page that tries to demonstrate all of the functionality of this little rascal. Not brilliant design, but primo functionality.


If anyone has any comments or questions, email me at ms_@_mybluecoat.com (remove the underlines first...) or use the nifty comment deal that Foo has added to AlphaFilter...and keep those cards and letters coming.

Popular posts from this blog

A teacher's credentials...

Kristie told me this funny story..... The School has been blessed for years with a teacher who has dutifully taught yearbook class to the Senior High students. She abruptly resigned a couple of weeks ago, for personal reasons and nothing to do with the School or the job per se. In a mad scramble, the principal scrounged up a "suitable" replacement. This replacement was described as "a mom". Fast forward to yesterday. Kristie was doing new employee orientation and this new hire was present. Kristie did emphasize this person seems to be a terrific individual and an all around nice lady. When Kristie introduced herself she stated: "So you're the new yearbook teacher....nice to meet you". The new hire replied, "Thanks, do you know anything about this yearbook stuff? Because I don't..." Kelly signed up for yearbook this year...she already has Digital Photography, a questionable class and now Yearbook. Wednesday Elective Day is going t...