Matthew Boles

Subscribe to Matthew Boles: eMailAlertsEmail Alerts
Get Matthew Boles: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: ColdFusion on Ulitzer, XML Magazine

CFDJ: Article

Server to Client WDDX

Server to Client WDDX

The Web Distributed Data Exchange (WDDX) is an XML-based technology that allows different Web technologies to exchange data. One function of WDDX is to send ColdFusion queries to JavaScript for use on the client. This article details how to use this functionality with HTML SELECT statements, and assumes you're familiar with ColdFusion and HTML forms but not with WDDX and JavaScript.

WDDX, HTML forms, ColdFusion queries and JavaScript are combined to create a single page providing SELECT statements that are dynamically populated. In addition, one SELECT statement can be filled based on an earlier user selection on the same Web page. In essence, this makes queries that were traditionally available only on the server side now available on the client.

ColdFusion and HTML forms can be used together to dynamically populate SELECT controls from a ColdFusion query. When a user views this form, the query is performed; the SELECT statement contains the latest options from the table that was queried. To enhance this process you may want to populate a second SELECT statement based on a selection the user made from an earlier one. For instance, the first SELECT statement offered options for Colors and Trees (see Figure 1).

If the user selected Colors from the first SELECT control, the second will be populated with the options green, orange and red (see Figure 2).

If the user selected Trees, then the second SELECT will be populated with the options Fir, Hemlock and Pine (see Figure 3).

A common way to implement this behavior is on two separate pages since the first selection wouldn't be known until the user submitted the form. This article shows how to pass the second set of options to the client so the process can take place on a single page.

The first step in implementing this process is the conversion of a ColdFusion query to a JavaScript array (technically, the query is converted into a JavaScript recordset structure). Since you don't know which option a user will select from the first SELECT statement, you'll have to pass all queries that may be needed for options in the second SELECT statement. To do this you'll use WDDX.

WDDX allows you to exchange data between ColdFusion and JavaScript. The following steps are necessary:

1. Perform a ColdFusion query.
2. Use WDDX to convert the query into a JavaScript array.
3. Place the JavaScript array definition in the JavaScript section of the page.

You may then use the data as you would a JavaScript array.

Step 1, performing queries, is familiar to any ColdFusion developer. For this example consider two queries - qryColor and qryTree. Each query reads three rows of data containing a primary key value, a color name or primary key value, and a tree name.

Step 2 uses WDDX to convert the ColdFusion queries to JavaScript usable variables. This is a two-step process, but we'll use a shortcut and make it a one-step process. You need to convert the ColdFusion query into a WDDX packet, then convert the packet into a JavaScript variable. This process uses the <CFWDDX> tag. Without using the shortcut the two lines of code are:

<CFWDDX ACTION="CFML2WDDX"
INPUT="#qryTree#"
OUTPUT="jsTreeTemp">

<CFWDDX ACTION="WDDX2JS"
INPUT="#jsTreeTemp#"
TOPLEVELVARIABLE="jsTreeTLV"
OUTPUT="jsTree">

The first statement uses the attribute ACTION="CFML2WDDX". This converts or, in WDDX lingo, serializes the ColdFusion query into a WDDX packet stored under the name jsTreeTemp. It isn't necessary to understand the actual contents of the packet in order to use it, but, for the curious, see Listing 1 for the query containing the tree primary keys and names converted to WDDX. The indentation used in the packet is for readability only; the actual packet output contains no line breaks.

The second statement uses the ACTION="WDDX2JS" attribute to change the WDDX packet to a JavaScript variable, i.e., deserializes from WDDX to JavaScript. The actual variable type is defined in a file named wddx.js that you'll reference on the page that's installed with ColdFusion. In the code for this page the reference looks like:

<SCRIPT TYPE="text/javascript"
LANGUAGE="JavaScript"
SRC="/CFIDE/scripts/wddx.js">
</SCRIPT>

If this isn't included, you'll probably see an error:

'WddxRecordset' is undefined.

The input for the tag is the WDDX packet created in the first CFWDDX tag. The OUTPUT attribute holds the definition of the JavaScript variable that we'll use in the JavaScript section of the code. The TOPLEVELVARIABLE attribute defines the name of the variable to be used in the JavaScript code.

You need only one tag to perform both these steps using the CFWDDX tag:

<CFWDDX ACTION="CFML2JS"
INPUT="#qryTree#"
TOPLEVELVARIABLE="jsTreeTLV"
OUTPUT="jsTree">

This converts the ColdFusion query directly into the JavaScript variable and Step 2 is completed.

Step 3 is quite simple. You simply need to start the script section of the page using:

<SCRIPT LANGUAGE="JavaScript">

and then use CFOUTPUT to put the JavaScript variable definitions into the script section of the page. When you use the code:

<CFOUTPUT>#jsTree#</CFOUTPUT>

you place the JavaScript variable definition on the page. As with the actual format of the WDDX, it's not essential to understand the format of the variable declaration. But again, for the curious, the qryTree query becomes the JavaScript variable defined by:

jsTreeTLV=new WddxRecordset();
_t2=new Array(); _t2[0]=1;_t2[1]=3;_t2[2]=2;
jsTreeTLV.tree_id=_t2;_t2=new
Array();_t2[0]="Fir";_t2[1]="Hemlock";_t2[2]="Pine";
jsTreeTLV.tree_name=_t2;_t0=null;_t1=null;_t2=null;

In the actual code there are no line breaks, just as in the WDDX packet.

To help you understand how to use this definition, think of the query being converted into an array of the general form TopLevelVariableName.columnname[i]. Specifically, the query qryTree generates:

jsTreeTLV.tree_id[0]=1 (the primary key value) and
jsTreeTLV.tree_name[0]=Fir,
jsTreeTLV.tree_id[1]=3 (the primary key value) and
jsTreeTLV.tree_name[1]=Hemlock,
jsTreeTLV.tree_id[2]=2 (the primary key value) and
jsTreeTLV.tree_name[2]=Pine.

Since JavaScript is case sensitive, it's important to note that in the deserialization process the TOPLEVELVARIABLE attribute value of the CFWDDX tag keeps its case, and the field names from the query become lowercase; the case of the columns from the query doesn't matter. Note that the index for the array starts at 0 and not 1.

With the ColdFusion queries converted into JavaScript variables, it's time to look at the JavaScript that uses these variables.

The goal of the page now is to make these events occur:

1. When the user selects an option from the Select1 form control, a JavaScript function called choosenext() is called.

2. The choosenext() function decides which option the user selected.

3. The choosenext() function fills the DynamicSelect form control with the appropriate query information passed into the JavaScript.

To accomplish Step 1, a SELECT statement is created with the form using HTML:

<SELECT NAME="Select1" onChange=choosenext()>

The statement uses the JavaScript onChange event handler. This means that when a selection is made from the SELECT box, an event occurs (onChange) and the function called choosenext() is called (defined in the SCRIPT section of the page). This happens as soon as the user makes a change in the SELECT statement before the form is even submitted.

The job of the choosenext() function is to populate the SELECT statement that was created with HTML:

<SELECT NAME="DynamicSelect">
<OPTION VALUE="">
</OPTION>
</SELECT>

Nonbreaking spaces are used because Navigator won't dynamically enlarge the width of the box, but if this isn't done the options won't display correctly .

Now Step 2 needs to be completed, i.e., deciding which selection was made in the Select1 form control. This can be done by using the JavaScript "if" statement in the form.

if (document.TestForm.Select1.selected
Index==0)
if (document.TestForm.Select1.selected
Index==1)
if (document.TestForm.Select1.selected
Index==2)

This statement takes advantage of the Document Object Model (DOM). The document object is the entire page with the "TestForm" form that contains the "Select1" SELECT control. This object, which can be referenced by the name "document.TestForm.Select1", has a property called selectedIndex. Then it's all put together to see which option the user chose from the SELECT control and guides the program flow into the appropriate section of the choosenext() function.

Now it's time to get Step 3 done: filling the "DynamicSelect" SELECT control with the needed query results (which, as you recall, have been converted into JavaScript variables). First you need to delete any options in the SELECT control by setting the number of options to 0. Again, we need to take advantage of the DOM and use the code:

document.TestForm.DynamicSelect.
length=0;

This sets "DynamicSelect's" length to 0. All SELECT controls have a length property just as they have a selectedIndex property, which we used earlier.

Next, a loop will be used to put the correct number of options into the SELECT statement. The number of times to loop will be determined by the statement:

for (var RowNum=0; RowNum<jsTreeTLV.tree_id.length; RowNum++)

This creates a loop counter called RowNum that starts at 0 since the index of the array starts at 0. The loop will stop when RowNum exceeds the length of the array, which is equivalent to the number of records read in the ColdFusion query. This number is retrieved from a property of a JavaScript array called length and is referenced by the code:

jsTreeTLV.tree_id.length

The last part of the loop statement, RowNum++, increments RowNum by one each time through the loop.

Finally, the code that creates the options and puts them into the DynamicSelect form control is used. To accomplish this we use the following lines of code:

NewOpt=new Option;
NewOpt.value=jsColorTLV.color_id
[RowNum];
NewOpt.text=jsColorTLV.color_name[RowNum];
document.TestForm.DynamicSelect.
options[RowNum]=NewOpt;

Each time through the loop a new option is created using the JavaScript operator new. This creates a new instance of an option to be placed within the SELECT control. Then the option needs to be given a value for the VALUE attribute and text to be displayed to the user. This is accomplished using the code:

NewOpt.value=jsColorTLV.color_id
[RowNum];
NewOpt.text=jsColorTLV.color_name[RowNum];

This code uses the DOM and the value and text properties of the options, which is itself a property of the SELECT statement. Each time through the loop the next values from the JavaScript array are taken and assigned to an option. The last line of code:

document.TestForm.DynamicSelect.
options[RowNum]=NewOpt;

assigns the new option created to the list of previous options. The options property is actually an array that holds the options. That's why the loop will generate code that will fill the array using options[0], options[1], options[2], etc.

Just before the choosenext() function is exited, the DynamicSelect statement needs to have the first option selected so it'll appear in the list of options. Otherwise a blank option will be used. This is done with the code:

document.TestForm.DynamicSelect.
options[0].selected = true;

This code makes another reference to the DOM and sets the Boolean property selected (of the options property) to true. This puts the first option in the SELECT control. Remember that the first element of the array has an index of zero.

With the selection of an option from the first SELECT control, the second SELECT control is dynamically populated.

More Stories By Matthew Boles

Matthew Boles has been doing ColdFusion training for Allaire for nearly two years and has worked with ColdFusion since version 1.5. He is also a certified Novell and Microsoft instructor, although he now devotes his professional time to ColdFusion training and consulting. msboles@mindspring.com

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.