| By Grant Szabo | Article Rating: |
|
| March 9, 2004 12:00 AM EST | Reads: |
25,799 |
Have you ever wanted to write an HTML-based form that would behave more like a Win-32 "fat client" application than a Web page? Something like a combo box-like control where the user can type in a text field and have a list-box containing matching records auto-filter based on what was typed?
Maybe you're not thinking of anything that fancy. What about simple drill-down selectors where a user chooses a manufacturer, and, based on that selection, a second drop-down list instantly populates, containing just the products offered by that chosen manufacturer? What's more, you want to accomplish this without having to refresh the browser page and you aren't planning on implementing Flash Remoting.
Enter JavaScript Remote Scripting (JSRS). JSRS is a client-side JavaScript library that uses dynamic HTML elements to make hidden remote procedure calls back to the Web server. JSRS was written by Brent Ashley in 2000 (www.ashleyit.com/rs/). It is open source and free of charge to anyone who wants to use it. It is no longer supported by Brent, but there is a community of active developers who use the library, and help is often available from these folks on Brent's forums. JSRS works asynchronously and, according to Brent, is known to work on Win9x, WinNT/2000, WinXP, Unix/Linux/BSD, and Mac with IE4+, NS4.x, NS6.x, Mozilla, Opera7, and Galeon. There are server-side implementations for ASP, ASP.NET, ColdFusion, PerlCGI, PHP, Python, and JSP (servlet).
This article will provide concrete steps on how to implement JSRS with ColdFusion MX so you can begin taking advantage of this great tool in your ColdFusion projects.
I've targeted this article for intermediate to advanced ColdFusion developers who have intermediate skills with JavaScript and DHTML. If you haven't done a ton of work with JavaScript or DHTML, don't sweat it. There should be enough code here to help you get your first JSRS script working even without that knowledge. Working on a JSRS implementation will help you develop these skills and you'll have fun at the same time!
Examples on the Web
First, let's start with some examples so you can see this technology in action before you start implementing it. Every bit of JSRS code that I've implemented has been for corporate intranets or secure extranet applications, so unfortunately I can't show you anything running in production. I've got a couple of examples on my home page for you to check out, though.
Navigate to www.quagmire.com/whiteboard.aspx to see the examples.
Getting Started
The first thing you'll need to do is download the JSRS package for ColdFusion MX and review the source code. It's important to understand the concept of how JSRS works before attempting to implement anything. There are a few moving parts to get familiar with.
Download the JSRS source code example from www.sys-con.com/coldfusion/sourcec.cfm. The CFMX package is available without the Autofilter example at www.quagmire.com/cfm/jsrsCFMX.zip" .
The following core files are included in the distribution. Let's get familiar with what they are and what they do:
- jsrsClient.cfm: The jsrsClient.cfm file is where everything starts and finishes. The critical elements that you must include in this CF template are:
- Two JavaScript functions: a calling function and a callback function for each JSRS call
- At least one HTML form element that uses a JavaScript event to fire your calling function
- A <DIV> tag to which you will write the results of your server-side call - jsrsSvr.cfm: This template processes your server-side call. This script executes in a different thread than jsrsClient.cfm.
- jsrsServer.cfc: This ColdFusion component is used to write the results of jsrsSvr.cfm to your callback function in jsrsClient.cfm. You instantiate this object and invoke the jsrsDispatch() method to do this.
- jsrsClient.js: This is the core JavaScript library written by Brent Ashley and must be included for any of this to work. You include this file in the <head> section of jsrsClient.cfm.
- License.txt: Brent Ashley's No Nonsense Copyright and License for JSRS.
1. Within jsrsClient.cfm, an HTML form element, such as a text box is wired to a JavaScript event, such as an onKeyUp() event. This onKeyUp() event fires when the user types something in the text box. The contents of the text box are passed to the JavaScript function that is executed when the onKeyUp() event fires.
2. The JavaScript function executes a method in Brent Ashley's jsrsClient.js JSRS library named jsrsExecute(). One of the parameters passed into jsrsExecute is the name of the JavaScript callback function. You'll write your callback function in JavaScript and place that function directly beneath the function you used to call jsrsExecute(). Both of these functions will reside in jsrsClient.cfm.
3. jsrsClient.js is a blackbox. It processes the information passed in via jsrsExecute(), then does an HTTP Get or Post (depending on the client browser) to the jsrsSvr.cfm file. jsrsClient.js exists on the client (it's sitting in the browser cache). It does a post to the server. This is key to understanding what is happening with JSRS. This post is a new thread that the user executed without even knowing it.
4. jsrsSvr.cfm is a server-side processing form that you write to handle the event. jsrsSvr.cfm is a standard CFML template with some special code for handling the information it receives from jsrsClient.js. Here's where you can make a database call, execute a stored procedure, or call into one of your business objects (a CFC, for example) to do something, like return a recordset. You then need to convert this recordset into a delimited list. Once you have this list constructed, you invoke the jsrsDispatch() method of the jsrsSvr.cfc object.
5. jsrsSvr.cfc is a CFC that I wrote that takes your delimited list and writes it back to your callback function (see step 2). The process flow is now back where it all started - on jsrsClient.cfm, but now it's in your callback function.
6. jsrsClient.cfm callback function uses the JavaScript split function to split your list into a JavaScript array. You then take these values from the array and write them into an HTML string. A little secret about JavaScript that you should know - JavaScript processes arrays about 10x faster than it processes lists. Thus, you should use arrays wherever possible. It's easy to take the array and convert it into a string by using the join method of the array object. See the jsrsClient.cfm code you downloaded for how to do this in the callback function.
7. Once you have your HTML string ready for display, simply write this to a <DIV> tag using the innerHTML property.
Putting It All Together
First, place jsrsClient.js somewhere on your Web site. I usually create a folder named "javascript" off of my webroot and place all .js files in there. That's a great place to put jsrsClient.js.
Next, place jsrsSvr.cfc in your components directory - the directory where you are storing all of your CFCs for your application.
jsrsClient.cfm
Now, start working on your jsrsClient.cfm page (you don't need to name it jsrsClient.cfm - name it whatever you want). First, set an include reference in the <HEAD></HEAD> section of your template like so:
<html>
<head>
<title>jsrsClient</title>
<script language="javascript" src="/javascript/jsrsClient.js"></script>
</head>
Next, build your Web form. Here is the HTML from the Autofilter jsrsClient.cfm file:
<form>
<table>
<tr>
<td class="font1Bold">Product Name: </td>
<td><input type="text" size="15" name="productNameTextBox" class="font1"
onKeyUp="fcnGetProduct(this)"></td>
</tr>
<tr>
<td></td>
<td><div id="divListBox">
<select name="ProductId" size="5" class="font1">
<cfoutput query="RS">
<option value="#ProductId#">#ProductName#</option>
</cfoutput>
</select>
</div></td>
</tr>
</table>
<div id="divDetail"></div>
</form>
Next, you'll write your calling and callback functions (nest these inside <script language="javascript"></script> tags). The Autofilter example makes two JSRS calls so there are two calling functions and two callback functions. The first RPC call fires with the onKeyUp() event of the text box. The second RPC call fires when the user selects a product in the list box.
//autofilter JSRS Calling Function (Fires from onKeyUp() event)
function fcnGetProduct(obj) {
var aParams = new Array(1);
aParams[0] = obj.value;
jsrsExecute( '/jsrs/jsrsSvrMX.cfm', fcnCallbackGetProduct,
'getProduct', aParams, 0); }
Note the parameters being passed to jsrsExecute. These parameters are:
- '/jsrs/jsrsSvrMX.cfm': This is the template that will handle the server-side processing for JSRS.
- 'fcnCallbackGetProduct': This is the name of the callback function.
- 'getProduct': This is the <cfcase> action argument that will be used in jsrsSvr.cfm.
- 'aParams': jsrsExecute() requires that parameters be passed in via a JavaScript array.
- '0': This is a bit field set to either 0 or 1. If you set it to 1, JSRS will make an iFrame visible that it places on jsrsClient.cfm, providing a "window" to execute your jsrsSvr.cfm template. When you are debugging your jsrsSvr.cfm page, you'll set this value to 1 so that you can see any errors that your jsrsSvr.cfm page might be throwing. Once you have everything debugged, set this value back to 0 and effectively hide all the back-end processing under the blanket.
//autofilter JSRS Callback Function
function fcnCallbackGetProduct(optsStr) {
aCallback = optsStr.split(delim); //split takes the string/list
//returned from the RPC and converts it into an array
var strHTMLArr = new Array(); //instantiate array to hold HTML
var strHTML = ""; //initialize strHTML
strHTMLArr[0] = '<select id="ProductId" name="ProductId" size="5"
class="font1">';
for (i=1; i<=aCallback.length; i++) {
if(i % 2 == 0) {
//value of loop is even
strHTMLArr[i] = aCallback[i-1] + '</option>';
}
else {
//value of loop is odd
if (i == 1) {
//highlight the first item in the array
strHTMLArr[i] = '<option value="' + aCallback[i- 1] + '" SELECTED>';
}
else{
strHTMLArr[i] = '<option value="' + aCallback[i-1] + '">';
}
}
}
strHTMLArr[i++] = '</select>';
strHTMLArr[i++] = '<input type="button" value="View Detail" class="submit"
onClick="fcnGetProductDetail()">';
strHTML = strHTMLArr.join(""); //join() is the opposite of split().
//It converts an array to a list using a delimiter, in this case ""
//(no delimiter)
document.getElementById("divListBox").innerHTML = strHTML;
//write
//out the completed html string to the div tag
}
Let's walk through the callback function. The first line is receiving our delimited list back from jsrsDispatch() (see jsrsSvr.cfc). We take that value, which I call "optsStr", and use the JavaScript split method to convert the list to a JavaScript array. Next, I initialize two JavaScript vars, strHTMLArr, and strHTML. strHTMLArr is a JavaScript array that will be used to hold HTML strings.
Now, use a JavaScript FOR LOOP to iterate through the callback array, writing out HTML along the way. Once the loop is finished, I write in an HTML button (that will call the next JSRS calling function) and then use the join method of the callback array object. By specifying "" as the parameter of the join method, I am effectively concatenating the JavaScript Array into a string as opposed to a list.
The remaining JavaScript functions are not presented here, but are available for download from www.sys-con.com/coldfusion/sourcec.cfm.
jsrsSvr.cfm
Now you'll write your jsrsSvr.cfm file. You can put this file in the same directory as your jsrsClient.cfm file or you can place it out in some global directory where you will place all of your JSRS methods (such as /jsrs located off of your webroot). If you use the latter method, you can use just this one single jsrsSvr.cfm file to handle requests from all of your jsrsClient.cfm files used throughout your application - you just include a new <cfcase></cfcase> statement for each JSRS RPC.
I would suggest that you simply use the jsrsSvr.cfm file included in the package you downloaded and modify the code inside the <case></case> statements. Everything else can be left alone - and should be left alone unless you really know what you're doing. I've optimized the code in this file to run as fast as possible - using CF arrays.
Here's the <cfcase> for getProduct. Remember "getProduct" was a parameter that was passed into jsrsExecute() in the calling function.
<cfcase value="getProduct">
<cfscript>
//get recordset
objNorthwind = CreateObject('component', 'quagmireV3.components.northwind');
RS = objNorthwind.getProduct(aParams[1]);
//build return string
for(i=1; i LTE RS.RecordCount; i=i+1) {
request.rStr = request.rStr & RS.ProductId[i] & variables.delim &
RS.ProductName[i] & variables.delim;
}
//remove trailing delimiter
variables.rStrLen = Len(request.rStr);
if(variables.rStrLen GT 0)
request.rStr = RemoveChars(#request.rStr#, #variables.rStrLen#, 1);
</cfscript>
</cfcase>
There is no requirement to use CFScript. If you're not comfortable with CFScript, use regular CFML. I prefer CFScript syntax over regular CFML so I tend to use it a lot in my ColdFusion development.
Walking through this code, I'm first getting a recordset of matching products using the "getProduct" method of my northwind.cfc. Here's that function:
<cffunction name="getProduct" access="public" returntype="query">
<cfargument name="ProductName" type="string" required="yes">
<cfstoredproc procedure="spNW_Sel_Products_findByProductName"
datasource="#this.DSN#">
<cfprocparam type="In" cfsqltype="CF_SQL_VARCHAR" variable="@ProductName"
value="#arguments.productName#" null="No">
<cfprocresult name="RS1">
</cfstoredproc>
<cfreturn RS1>
</cffunction>
Next, I build a return string by using a FOR loop (you may use <cfoutput></cfoutput> instead if you wish). What's important is that you concatenate the delimited list.
Last, I whack off the trailing delimiter. The variable request.rStr is now ready to be handed off to jsrsSvr.cfc so that it can be posted back to our callback function.
Conclusion
I hope that this concise overview of implementing JavaScript Remote Scripting will help advance your ability to develop UIs for the Web using ColdFusion. JSRS is a very powerful and effective tool for building Win32-like functionality into your Web forms without resorting to Flash.
Published March 9, 2004 Reads 25,799
Copyright © 2004 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Grant Szabo
Grant Szabo is a senior application developer for Global Cloud, Ltd.
(www.globalcloud.net) where he delivers Microsoft .NET
and ColdFusion solutions for Global Cloud's clients. Grant worked for Allaire
(later Macromedia), as director, worldwide professional services for nearly two years in
2000-2001. He is certified in CF5 and CFMX and holds numerous
Microsoft certifications including MCSD, MCDBA, MCSE, and MCSA.
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Adobe Reader Sued
- Adobe May Cooperate with Apple to Transplant Flash Player to iPhone
- Adobe Flex Developer Earns $100K in New York City
- Adobe LiveCycle Enterprise Suite 2 for Cloud Computing
- Adobe Cans Another 9% of its Workforce
- Adobe Betas Target RIAs and Cloud Computing
- Adobe MAX 2009 Online
- Thinking of Flex in London
- Moyea DVD4Web Converter V2.0 Converts DVD to FLV Fast and Synchronously with Watermarks
- Adobe & Salesforce Cut Cloud Deal
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Eval JavaScript in a Global Context
- Fig Leaf Software to Exhibit at Government IT Conference & Expo
- Is Microsoft as Free as Open Source?
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Adobe Reader Sued
- The Planet Named “Bronze Sponsor” of Cloud Computing Expo
- Microsoft Expression Web Has Got Game
- Adobe May Cooperate with Apple to Transplant Flash Player to iPhone
- Bruce Chizen Joins Voyager Capital as Venture Partner
- My Top Seven Wishes From Adobe MAX 2009
- Adobe Flex Developer Earns $100K in New York City
- The Next Programming Models, RIAs and Composite Applications
- Where Are RIA Technologies Headed in 2008?
- Constructing an Application with Flash Forms from the Ground Up
- AJAX World RIA Conference & Expo Kicks Off in New York City
- CFEclipse: The Developer's IDE, Eclipse For ColdFusion
- Personal Branding Checklist
- Adobe Flex 2: Advanced DataGrid
- Has the Technology Bounceback Begun?
- Building a Zip Code Proximity Search with ColdFusion
- i-Technology Viewpoint: We Need Not More Frameworks, But Better Programmers
- The Asynchronous CFML Gateway
- Web Services Using ColdFusion and Apache CXF





































