Universally Related Popup Menus AJAX Edition: Part 3 / Page 2 | WebReference

Universally Related Popup Menus AJAX Edition: Part 3 / Page 2

[previous] [next]

Universally Related Popup Menus AJAX Edition: Part 3 [con't]

This brings us to our special setter method for the base list's DEFAULT_INDEX property (line 112). We need to call this function every time that the page loads, but only after the list has been populated, so we can validate the default index against the options length. Getting the value from the hidden field is nearly the same than for other optional properties except for the conversion to an integer (line 118). To do the conversion, we can use the native parseInt() JavaScript function. It returns the first number that it locates in a String. Spaces are allowed, but if it encounters any characters that cannot be converted into some kind of numeric value, it returns NaN, which stands for "Not a Number". Once we've validated the number, we can safely set the DEFAULT_INDEX. We have to add the BLANK_ENTRY as well because it's not included in the index (line 124). Thus, if BLANK_ENTRY is set to true, we want to add one to the number. This is easily done, because JavaScript converts a false Boolean to 0 and a true one to 1. Unlike other setters, the setDefaultIndex() function also sets the base list's selectedIndex without the accompanying fireOnChangeEvent() function (line 124). We don't need to since the fillListCallBack() function already calls it (line 279).

Like the getXmlHttpInstance() method of the XmlHttpObjectManager, this function is also of the mutating variety. Instead of checking for a condition to determine whether or not it's the first time through the fillListCallBack() function, it's far easier to simply call the function every time and let it take care of what it needs to. In this case, it simply resets itself to a bare-bones function that does absolutely nothing (line 128)!

The onchange event will cause setSubList() to execute (line 292), since we bound it in the bindOnChangeFunctionsToListElement() function (line 60). It populates the child lists. To do this, it uses the next property reference to its linked URPM (line 294). There's more browser specific code here to deal with a Safari bug where it has a nasty habit of calling the onchange event when it shouldn't! Since we can't be certain that an onchange event has actually occurred, we have to store the last selectedIndex and manually verify that it has changed (line 297). Before we go about retrieving the new items to put in the sublist, we clear the child list so we don't have any remaining garbage in there (line 310).

The next step depends on whether or not a database item has been selected from the parent list. For example, the blank entry would not necessitate a server call. We can test for an index which is equal to or greater than the BLANK_ENTRY variable because JavaScript automagically promotes a Boolean to an integer when comparing to an integer (line 312). Since the selectedIndex property is zero-based, a value of one would be larger than the BLANK_ENTRY's index. In that case, the callServer() function would be executed within the sublist's scope, with the selected value as the parameter (line 314). If the blank entry was selected, we would still fire the onchange event so that the next list will be cleared (line 318).

Chain of Function Calls

That is what it takes to set up the lists when the page first loads. Now we'll look at what happens when you select an item from a list.

The Onchange Event

Way back in the bindOnChangeFunctionsToListElement() function, the setSubList() function was bound to the onchange event. Getting the sublist is a snap because every URPM that has one contains a pointer to it in the next property. Any time the user selects an item from a list, it sets off a chain of events that updates all the URPMs passed to the initLists() function during the document onload(). The chain of function calls during normal execution of the onchange event is shown at the right.

The Reset Event

When the Reset button is clicked, it calls the public resetLists() function (line 351), which in turn calls the base list's reset() method (line 105). It sets the default index (line 107) and calls the fireOnChangeEvent() function so that the child lists are set accordingly (line 109).

The Server-side ASP Code

The getListItems.asp script is responsible for generating JavaScript code to load the new items into each list. As mentioned at the start of the article, it turns out that this is a far easier task that generating an XML-formatted representation of all the URPMs relationships.

The first several lines are the same as the ASP script that supplied the Version III URPM list items. The first line sets the language to VBScript (line 1). The other options are PerlScript and JavaScript. The Option Explicit directive forces variable declaration using the Dim statement (line 3). There are three constants in the script. Unlike JavaScript, these really are constants! They have to be set when you declare them using the Const keyword. The first one sets the default file dsn (line 5). If you don't supply one in the HTML page, it will look for one called "URPMs.dsn" in a folder called "URPMs dB" one level above the "WWWRoot". The next two store the column indexes for the ID (line 6) and description fields (line 7).

The ID should always be the first field returned by your queries, followed by the description.

The next line declares our global variables: oConn holds the database connection, cmd stores the proc command, and rs contains the recordset. responseString is the script output to be returned to the browser. FileDSN is the variable which is used by the Connection object (line 9). It may or may not contain the FILE_DSN constant, depending on whether or not one is supplied via an input parameter.

The On Error Resume Next line tells the script that we wish to continue with the script execution even when an error is encountered (line 11). Otherwise, it would exit and nothing would be returned to the AJAX XmlHttpReponse Object.

Set oConn = Server.CreateObject("ADODB.Connection") creates the database connection object (line 13).

The FileDSN string is set to the input parameter if there is one. Otherwise, we use our default (line 15).

Finally, we establish a database connection using the FileDSN as the argument (line 22).

The next section of code deals with the Command object. First, we set the cmd variable to a new Command object (line 24) and set its ActiveConnection to the current one (line 25).

The next three constants are used by the cmd object (lines 27,28, and 29). I chose to create my own rather than use the Microsoft ones for two reasons:

  1. VBScript does not recognize them as Visual Basic does.
  2. Although there is a workaround by including the "Adovbs.inc" in your script, the problem with that approach is that it's overkill for a mere three constants.

The next line sets the command type using one of our three constants. There are four types of command types:

Constant Description
adCmdText Evaluates CommandText as a textual definition of a command.
adCmdTable Evaluates CommandText as a table name.
adCmdStoredProc Evaluates CommandText as a stored procedure.
adCmdUnknown (Default) The type of command in the CommandText property is not known.

The name of the proc is set according to the list's name. The format is:

"get" + UpperCase First Letter + lowercase remaining letters (line 32). This system is especially well suited to single word names such as "Manufacturers" and "Models" like I used in the sample page. For multiple words with spaces, the names don't look as good!

If the list ID is supplied in the code parameter, a new parameter is created and appended to the Command's Parameter Collection (line 34). All the lists except the base one should supply this value in the onchange event. The CreateParameter() function (line 36) takes five arguments. The first one is the name of the parameter. This has to match the name in your query! The next two arguments accept enumerated values, so I stored the values in the adVarChar and adParamInput constants for readability. The second argument sets the parameter data type. adVarChar is database parlance for a string. The third argument tells the function whether the parameter is of the input or output type. adParamInput denotes that the parameter is for input. The fourth argument is the length of the parameter. Ten is a safe length since the numeric ID will be passed to the server as a string. Hence, the length would be the number of digits in the ID. Finally, the last argument is the value of the parameter.

We then execute the proc, storing the results (line 39).

Now that we've got the list items, we have to convert the recordset because the AJAX XmlHttpResponse object is expecting a string to be returned. To do that, we first have to set the Charset (line 41). In IIS 5, the default charset is "utf-8". This doesn't accommodate French Characters. Why would we care about French characters? We have to be prepared because they do make an appearance in English from time to time. In the context of the automobile test lists, the "Mazda Protogé" requires the correct charset to display the last character. The one we want is "ISO-8859-1".

[previous] [next]