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
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
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).
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)!
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).
selectedIndexproperty 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
onchangeevent 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
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
Option Explicit directive forces variable declaration 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.
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
Set oConn = Server.CreateObject("ADODB.Connection") creates the database connection object (line 13).
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:
- VBScript does not recognize them as Visual Basic does.
- 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:
||Evaluates CommandText as a textual definition of a command.|
||Evaluates CommandText as a table name.|
||Evaluates CommandText as a stored procedure.|
||(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
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
adParamInput constants for readability. The second argument sets the parameter data type.
adVarChar is database parlance for a string.
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".