Ajax (which stands for "Asynchronous JavaScript and XML") is not a product or a technology. Actually, it's not any single thing you can point to. Rather, it is a technique that combines DHTML and JavaScript with the (until fairly recently unnoticed and unused) ability of web browsers to make asynchronous HTTP calls.
If that sounds obscure, don't worry. What it simply means is that JavaScript (the scripting language supported by just about every web browser) has the ability to make web page requests under programmatic control, requests that don't actually update or refresh the screen. By coupling that ability with some clever JavaScript and DHTML, web browser can be made to do some very interesting things, including addressing the scenarios just mentioned.
And, as you've come to expect from ColdFusion, if you want to leverage Ajax controls within your own applications, ColdFusion 8 can help. In this article, I show you how to do just that.
To complete this tutorial you will need to install the following software and files:
Download and install the trial of ColdFusion 8. During the installation process, the installation wizard will ask if you want to install sample code. Select this option and the data sources referred to in the code samples in this article will already be defined in your ColdFusion administrator. You need only to create the CFML files in the web root of your server to run these code samples.
Let's start by coding an auto-suggest control. Auto-suggest is a modified text input box, one that displays suggestions as the user types. The auto-suggest control in ColdFusion 8 can be used in two ways, with local client-side data, and with asynchronous calls back to ColdFusion. I'll be looking at both.
Here's a simple client-side data example (which uses one of the ColdFusion 8 example databases, so this should work for you as is):
<!--- Get data --->
<cfquery datasource="cfartgallery" name="data">
SELECT artname
FROM art
ORDER BY artname
</cfquery>
<!--- The form --->
<cfform>
Art:
<!--- Populate auto-suggest control --->
<cfinput type="text"
name="artname"
autosuggest="#ValueList(data.artname)#">
</cfform>
This form displays a simple text box, but as text
is entered, suggestions are displayed. The list of suggestions is passed to the autosuggest attribute, which
accepts a comma-delimited list. The list could be hardcoded, but here ValueList() is being used to
dynamically build a list based on a prior database lookup.
This is not an Ajax control in that lookups are not asynchronous, there is no communication back to the server to retrieve data—all data is local. This is actually a preferred form of auto-suggest for smaller lists.
For longer lists, asynchronous interaction is indeed preferred, and the auto-suggest control supports this by allowing asynchronous calls to a ColdFusion component. Here is a sample CFC:
<cfcomponent
output="false">
<cfset THIS.dsn="cfartgallery">
<!--- Lookup used for auto suggest --->
<cffunction name="lookupArt" access="remote" returntype="string">
<cfargument name="search" type="any" required="false" default="">
<!--- Define variables --->
<cfset var data="">
<!--- Do search --->
<cfquery datasource="#THIS.dsn#" name="data">
SELECT artname
FROM art
WHERE UCase(artname) LIKE Ucase('#ARGUMENTS.search#%')
ORDER BY artname
</cfquery>
<!--- And return it --->
<cfreturn ValueList(data.artname)>
</cffunction>
</cfcomponent>
This CFC has a single method named lookupArt that accepts a string
and performs a query to find all matches that start with the specified value.
Auto-suggest accepts a ColdFusion list, and the code uses ValueList() to create a list of
query column values which are then returned.
Now for the modified form code to use this CFC and method:
<cfform>
Art:
<cfinput type="text"
name="artname"
autosuggest="cfc:art.lookupArt({cfautosuggestvalue})">
</cfform>
Here the autosuggest attribute specifies a CFC, and as the CFC (I
named it art.cfc) is in the current folder, no path needs to be specified. When
a user enters a value, generated JavaScript code triggers an asynchronous call
to the lookupArt method in art.cfc. The
code, {cfautosuggestvalue} gets automatically
replaced with whatever value the user has entered, and that value is then used
by the CFC in the lookup. When results are returned, the auto-suggest list gets
populated.
Auto-suggest does not get any cleaner and simpler than this.
Many of you
have built related select controls, forms with two (or more) drop-down Ajax SELECT controls, where making
a change in one control causes the available selections in the related control
to change. For example, selecting a category in one control displays category
products in a related control, or selecting a state in one control updates a
related control with the cities in that state.
These controls are typically implemented using client-side JavaScript to process arrays of data embedded in the page itself. Every possible combination and option is embedded in JavaScript in the page, and client-side scripts update controls based on selection changes in other controls.
In ColdFusion 8, the new Ajax functionality
makes this kind of interface really easy, without requiring any client-side
scripting, and without requiring that all of the data be embedded in the
generated page. Rather, CFSELECT controls may be bound
to ColdFusion component methods that are asynchronously invoked as needed.
To demonstrate this, here is a complete working example that uses one of the example databases that comes with ColdFusion. First, here is the ColdFusion component:
<cfcomponent output="false">
<cfset THIS.dsn="cfartgallery">
<!--- Get array of media types --->
<cffunction name="getMedia" access="remote" returnType="query">
<!--- Define variables --->
<cfset var data="">
<!--- Get data --->
<cfquery name="data" datasource="#THIS.dsn#">
SELECT mediaid, mediatype
FROM media
ORDER BY mediatype
</cfquery>
<!--- And return it --->
<cfreturn data>
</cffunction>
<!--- Get art by media type --->
<cffunction name="getArt" access="remote" returnType="query">
<cfargument name="mediaid" type="numeric" required="true">
<!--- Define variables --->
<cfset var data="">
<!--- Get data --->
<cfquery name="data" datasource="#THIS.dsn#">
SELECT artid, artname
FROM art
WHERE mediaid = #ARGUMENTS.mediaid#
ORDER BY artname
</cfquery>
<!--- And return it --->
<cfreturn data>
</cffunction>
</cfcomponent>
This CFC contains two methods. The getMedia method returns all of
the media types in the art catalog database, and the getArt method accepts a media
id and returns any art that is associated with that passed id. Both methods simply
return queries containing the results.
Now for the form itself:
<cfform>
<table>
<tr>
<td>Select Media Type:</td>
<td><cfselect name="mediaid"
bind="cfc:art.getMedia()"
value="mediaid"
display="mediatype"
bindonload="true" /></td>
</tr>
<tr>
<td>Select Art:</td>
<td><cfselect name="artid"
value="artid"
display="artname"
bind="cfc:art.getArt({mediaid})" /></td>
</tr>
</table>
</cfform>
The form contains two CFSELECT controls, one named mediaid and the other named artid.
The mediaid control is bound to cfc:art.getMedia(), and to obtain the list
of media types to populate the control, the client makes an asynchronous call
to the getMedia method in art.cfc, and
populates the list with the returned query, using the columns specified in the VALUE and DISPLAY attributes. As you
would want this control to be automatically populated when the form loads, the bindonload attribute is set to true. This way, the getMedia() call is executed automatically
when the form loads.
The artid control is bound to the getArt method in art.cfc. This
method requires that a mediaid be passed to it, and so {mediaid} is used to pass the
currently selected value of control mediaid (the first CFSELECT control). Because these two controls are bound
together, the second dependant on the first, ColdFusion automatically generates
JavaScript code that forces artid to be repopulated with newly retrieved data whenever mediaid changes.
This example binds just two controls, but this
mechanism can be used to relate as many controls as needed, and not just CFSELECT controls either.
And this is just the start of it. By combining simple tag abstractions in ColdFusion, asynchronous calls to CFCs, and client-side bindings, coding Ajax-type user interfaces just got a whole lot simpler.