Client-Side Interactivity without Ajax
Keeping response times down and interactivity high has and always will be two important priorities with web interfaces of any kind. For standard html interfaces, Ajax is all the buzz and is great when it’s necessary to maintain interaction with live data. But when a static version of the data will do just fine, there’s at least one other alternative that you may want to consider….
The <CFWDDX> tag provides a way for us to bridge the gap between dynamic data processed on the server side (specifically, queries returned by CFQUERY), and Javascript on the client side. What this does is give us the ability to transform query results into a form that we can manipulate via javascript, opening up some very useful potential for interactive Dynamic HTML.
In a nutshell, the process goes like this:
In my example, I’m displaying a list of employees for the user to select from. What I want though is that whenever an employee is selected from the list, their detailed information is displayed to the side so that the user can determine if this is the right person or not. Like so:
(go 'head! click it!)
Ajax would work for this! Everytime an employee’s name is clicked on, we could make a call back to the server and grab that employee’s detailed information, then, using Javascript, output it to the designated portions of the template without ever reloading the page (yet still executing an HTTP call over the web). Or, we could just go ahead and retrieve the employees’ details during our initial query, since we had to retrieve their names and IDs anyway in order to produce the select list.
We could then take that query and create a Javascript-friendly version of it like so:
Within the body of our page, we’ll go ahead and create the select list just like we always do, only let’s add a function call to the onClick event that we can use to dynamically populate the targeted portions of our template:
Additionally, let’s create the placeholders for our employee details:
At this point, our query has been converted into a ‘WDDXRecordset’ object. Behind the scenes, it’s a complex Javascript array, but the nice folks at Adobe wrapped it up for us with a set of functions that make it easy to get at the data using JS. For example, to retrieve the first name from the first row, the call would look something like
Now all we need is a function to retrieve the employee details. It’s as simple as a Javascript FOR loop, leveraging the functions provided by the WDDXRecordset object:
You can grab the entire template here if you'd like to see it all together.
Gotchas/things to remember:
Doug out
The <CFWDDX> tag provides a way for us to bridge the gap between dynamic data processed on the server side (specifically, queries returned by CFQUERY), and Javascript on the client side. What this does is give us the ability to transform query results into a form that we can manipulate via javascript, opening up some very useful potential for interactive Dynamic HTML.
In a nutshell, the process goes like this:
- A CFQUERY tag is run;
- The result is fed to CFWDDX with the ACTION attribute set to CFML2JS, which produces a Javascript-ready recordset;
- The JS function that works with the recordset is created
In my example, I’m displaying a list of employees for the user to select from. What I want though is that whenever an employee is selected from the list, their detailed information is displayed to the side so that the user can determine if this is the right person or not. Like so:
(go 'head! click it!)
Ajax would work for this! Everytime an employee’s name is clicked on, we could make a call back to the server and grab that employee’s detailed information, then, using Javascript, output it to the designated portions of the template without ever reloading the page (yet still executing an HTTP call over the web). Or, we could just go ahead and retrieve the employees’ details during our initial query, since we had to retrieve their names and IDs anyway in order to produce the select list.
<!--- Using QuerySim to create the cfquery... --->
<cf_querysim>
UserInfo
userid,fname,lname,ext,title,dept,email
200|Joe|Blow|2235|Supervisor|Reporting|jblow@nowhere.com
300|Doug|Boude|2112|WaterBoy|Recreation|dboude@nowhere.com
100|Stan|Cox|1225|Developer|IT|scox@nowhere.com
400|Jim|Pickering|1513|Designer|IT|jpickering@nowhere.com
500|John|Smith|1112|CEO|Management|JASmith@nowhere.com
600|John|Smith|1123|Janitor|Building Maintenance|JLSmith@nowhere.com
</cf_querysim>
<cf_querysim>
UserInfo
userid,fname,lname,ext,title,dept,email
200|Joe|Blow|2235|Supervisor|Reporting|jblow@nowhere.com
300|Doug|Boude|2112|WaterBoy|Recreation|dboude@nowhere.com
100|Stan|Cox|1225|Developer|IT|scox@nowhere.com
400|Jim|Pickering|1513|Designer|IT|jpickering@nowhere.com
500|John|Smith|1112|CEO|Management|JASmith@nowhere.com
600|John|Smith|1123|Janitor|Building Maintenance|JLSmith@nowhere.com
</cf_querysim>
We could then take that query and create a Javascript-friendly version of it like so:
<script>
<cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
....
</script>
(Note that the attribute “TopLevelVariable” contains the value that will be used to name our recordset; that is the name it will be referenced as within our JS function.)<cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
....
</script>
Within the body of our page, we’ll go ahead and create the select list just like we always do, only let’s add a function call to the onClick event that we can use to dynamically populate the targeted portions of our template:
<select onClick="getUserInfo(this.value);" size="8">
<option value="" selected></option>
<cfoutput query="userInfo">
<option value="#userid#">#lname#, #fname#</option>
</cfoutput>
</select>
<option value="" selected></option>
<cfoutput query="userInfo">
<option value="#userid#">#lname#, #fname#</option>
</cfoutput>
</select>
Additionally, let’s create the placeholders for our employee details:
<table>
<tr>
<td><STRONG>First Name:</STRONG></td><td><span id="fname"></span></td>
</tr>
<tr>
<td><STRONG>Last Name:</STRONG></td><td><span id="lname"></span></td>
</tr>
<tr>
<td><STRONG>Title:</STRONG></td><td><span id="title"></span></td>
</tr>
<tr>
<td><STRONG>Department:</STRONG></td><td><span id="dept"></span></td>
</tr>
<tr>
<td><STRONG>Extension:</STRONG></td><td><span id="ext"></span></td>
</tr>
<tr>
<td><STRONG>Email:</STRONG></td><td><a id="email" href=""></a></td>
</tr>
</table>
(Note: In my example, I use SPAN tags as placeholders as well as an Anchor tag, but any valid document object (form fields, div tags, etc.) can be targeted as placeholders, as long as they have a unique ID value)<tr>
<td><STRONG>First Name:</STRONG></td><td><span id="fname"></span></td>
</tr>
<tr>
<td><STRONG>Last Name:</STRONG></td><td><span id="lname"></span></td>
</tr>
<tr>
<td><STRONG>Title:</STRONG></td><td><span id="title"></span></td>
</tr>
<tr>
<td><STRONG>Department:</STRONG></td><td><span id="dept"></span></td>
</tr>
<tr>
<td><STRONG>Extension:</STRONG></td><td><span id="ext"></span></td>
</tr>
<tr>
<td><STRONG>Email:</STRONG></td><td><a id="email" href=""></a></td>
</tr>
</table>
At this point, our query has been converted into a ‘WDDXRecordset’ object. Behind the scenes, it’s a complex Javascript array, but the nice folks at Adobe wrapped it up for us with a set of functions that make it easy to get at the data using JS. For example, to retrieve the first name from the first row, the call would look something like
Var thisName = getField(0,’fname’);
....
(Note that our first row is referenced with zero instead of one.)....
Now all we need is a function to retrieve the employee details. It’s as simple as a Javascript FOR loop, leveraging the functions provided by the WDDXRecordset object:
function getUserInfo(userid){
var i = 0;
for (i=0; i < users.getRowCount(); i++){// loop through our wddx recordset...
if(users.getField(i,'userid') == userid){//if the current record matches the ID passed in...
document.getElementById('fname').innerHTML = users.getField(i,'fname');
document.getElementById('lname').innerHTML = users.getField(i,'lname');
document.getElementById('title').innerHTML = users.getField(i,'TITLE');
document.getElementById('dept').innerHTML = users.getField(i,'dept');
document.getElementById('ext').innerHTML = users.getField(i,'ext');
document.getElementById('email').href = 'mailto:' + users.getField(i,'email');
document.getElementById('email').innerHTML = users.getField(i,'email');
return false; //ends this function call
}
}
//if we made it here, then we found no match in our loop above; just blank everything out.
document.getElementById('fname').innerHTML = "";
document.getElementById('lname').innerHTML = "";
document.getElementById('title').innerHTML = "";
document.getElementById('dept').innerHTML = "";
document.getElementById('ext').innerHTML = "";
document.getElementById('email').href = "";
document.getElementById('email').innerHTML = "";
}
(Note: Although our function only utilized the getField() and getRecordCount() methods, several more are available to us. You can take a gander at what else the WDDXRecordset provides at this LiveDocs link)var i = 0;
for (i=0; i < users.getRowCount(); i++){// loop through our wddx recordset...
if(users.getField(i,'userid') == userid){//if the current record matches the ID passed in...
document.getElementById('fname').innerHTML = users.getField(i,'fname');
document.getElementById('lname').innerHTML = users.getField(i,'lname');
document.getElementById('title').innerHTML = users.getField(i,'TITLE');
document.getElementById('dept').innerHTML = users.getField(i,'dept');
document.getElementById('ext').innerHTML = users.getField(i,'ext');
document.getElementById('email').href = 'mailto:' + users.getField(i,'email');
document.getElementById('email').innerHTML = users.getField(i,'email');
return false; //ends this function call
}
}
//if we made it here, then we found no match in our loop above; just blank everything out.
document.getElementById('fname').innerHTML = "";
document.getElementById('lname').innerHTML = "";
document.getElementById('title').innerHTML = "";
document.getElementById('dept').innerHTML = "";
document.getElementById('ext').innerHTML = "";
document.getElementById('email').href = "";
document.getElementById('email').innerHTML = "";
}
You can grab the entire template here if you'd like to see it all together.
Gotchas/things to remember:
- some web hosts don't provide access to the CFIDE/Scripts directory. If this is the case, then you'll need to grab a copy of wddx.js and put it in your webroot, then reference it within a script tag prior to creating any wddx-powered functions, like so:
<script src="wddx.js"></script>//< - - - making sure wddx.js is loaded...
<script>
<cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
function getUserInfo(userid){
...(remainder of js)
<script>
<cfwddx action="cfml2js" input="#UserInfo#" toplevelvariable="users">
function getUserInfo(userid){
...(remainder of js)
- When referencing query fields, the js is not case sensitive (a getField(1,'title') will work as well as getField(1,'TITLE') ); However, javascript itself IS case sensitive, so referencing the target span tag you must use the exact case of the ID (getElementById('title') is NOT the same as getElementById('TITLE') )
- The WDDXRecordset object contains a zero-based array, meaning that the index number of the first row will actually be referenced as zero rather than one (getField(0,’fname’) retrieves the value for first name from the first record)
Doug out
Subscription Options
You are not logged in, so your subscription status for this entry is unknown. You can login or register here.
Re: Client-Side Interactivity without Ajax
If you are willing to give up the convience methods such as getField() or getRecordCount(), you can replace WDDXRecordset with Object and completely removing any dependancy to the wddx.js library. For example:
The resulting object (in CF terminology) is a structure of arrays, each key in the struct represents a column and the array within represents the different rows, so to determine the record count you might say the following:
if( newJSVariable['MYCOLNAME'].length > 0 )
alert('We have records!');
The resulting object (in CF terminology) is a structure of arrays, each key in the struct represents a column and the array within represents the different rows, so to determine the record count you might say the following:
if( newJSVariable['MYCOLNAME'].length > 0 )
alert('We have records!');
Posted by JAlpino on September 19, 2006 at 10:04 AM
Re: Client-Side Interactivity without Ajax
The code didn't show up, let me try again:
<cfwddx action="CFML2JS" input="#attributes.var#" output="variables.retJSobj" toplevelvariable="newJSVariable" />
<cfset variables.retJSobj = replaceNoCase(variables.retJSobj,"wddxrecordset","Object","ALL")>
<cfwddx action="CFML2JS" input="#attributes.var#" output="variables.retJSobj" toplevelvariable="newJSVariable" />
<cfset variables.retJSobj = replaceNoCase(variables.retJSobj,"wddxrecordset","Object","ALL")>
Posted by JAlpino on September 19, 2006 at 10:06 AM
Re: Client-Side Interactivity without Ajax
nice
very nice....!!!
very nice....!!!
Posted by aajay on April 12, 2007 at 5:08 AM

