Spry AutoSuggest With Multiple Parameters

When loading datasets from the server for for the Spry AutoSugget widget, Spry passes the value of the search field as a parameter to the dataset URL. This works fine, but what if the suggestions you want to provide are based on a combination of form fields?

To give you a better of idea of what I mean, here is an example of a search form I was working on earlier today: (Note: this form doesn't work.)

Find users within of the following :

I wanted to provide suggestions for the search term, but I wanted to limit those suggestions to only the country (U.S. or Canada) and type (City or Postal Code) selected. So if a user selects U.S. Cities they should only get U.S cities as suggestions, no Canadian cities or postal codes of any kind. This turned out to be fairly easy but I though I'd share my approach in case others are interested.

Basically what I needed to do is append multiple parameters to the dataset URL. To accomplish this I added a new function to the Spry AugoSuggest widget named addReplaceParams:

Spry.Widget.AutoSuggest.prototype.addReplaceParams = function(params)
{
   
   var ds = this.dataset;
   ds.cancelLoadData();
   ds.useCache = false;
   
   this.setValue('');
   this.showSuggestions(false);
      
   var url = ds.url;
   
   for(var i=0; i < params.length; i++) {   
      url = Spry.Widget.Utils.addReplaceParam(url, params[i].param, params[i].value);   
   }      
   
   ds.setURL(url);
   ds.loadData();
   
};
In a nutshell this function clears the search term, hides suggestions and updates the AutoSuggest's dataset URL using an array of parameters passed to the function. The parameters are defined as simple JavaScript objects with two keys: param which is the name of the URL parameter, and value which is the value of the URL parameter. It does the actual parameter additon/replacement using the Spry.Widget.Utils.addReplaceParam() function. To update the URL using this function I wrote another function which I use as the onChange event handler for the country and type select elements.
function updateDsURL(f){
   
   var country = f.radiusCountry[f.radiusCountry.selectedIndex].value;
   var type = f.radiusType[f.radiusType.selectedIndex].value;
   
   var params = [];
   params[0] = {param:'radiusCountry',value:country};
   params[1] = {param:'radiusType',value:type};
   
   theSuggest.addReplaceParams(params);   
};

Putting it all together this is what it looks like:

<html>
<head>
<script language="JavaScript" type="text/javascript" src="includes/xpath.js"></script>
<script language="JavaScript" type="text/javascript" src="includes/SpryData.js"></script>
<script language="JavaScript" type="text/javascript" src="includes/SpryAutoSuggest.js"></script>
<link href="includes/SpryAutoSuggest.css" rel="stylesheet" type="text/css" />
<script language="JavaScript" type="text/javascript">
var ds1 = new Spry.Data.XMLDataSet("/suggestions.cfm","radiusvalues/radiusvalue");

function updateDsURL(f){
   
   var country = f.cboRadiusCountry[f.cboRadiusCountry.selectedIndex].value;
   var type = f.cboRadiusType[f.cboRadiusType.selectedIndex].value;
   
   var params = [];
   params[0] = {param:'radiusCountry',value:country};
   params[1] = {param:'radiusType',value:type};
   
   theSuggest.addReplaceParams(params);   
};


Spry.Widget.AutoSuggest.prototype.addReplaceParams = function(params)
{
   
   var ds = this.dataset;
   ds.cancelLoadData();
   ds.useCache = false;
   
   this.setValue('');
   this.showSuggestions(false);
      
   var url = ds.url;
   
   for(var i=0; i < params.length; i++) {   
      url = Spry.Widget.Utils.addReplaceParam(url, params[i].param, params[i].value);   
   }      
   
   ds.setURL(url);
   ds.loadData();
   
};
</script>
</head>

<body>
<form>
<p>
Find candidates within
<select name="radius">
   <option value="0" selected>Any Distance</option>
   <option value="1">1 mile</option>
   <option value="2">2 miles</option>
   <option value="3">3 miles</option>
   <option value="4">4 miles</option>
   <option value="5">5 miles</option>
   <option value="10">10 miles</option>
</select>
of the following
<select name="radiusCountry" onChange="updateDsURL(this.form);">
   <option value="U" selected>U.S.</option>
   <option value="C">Canadian</option>
   <option value="B">U.S. or Canadian</option>
</select>
<select name="radiusType" onChange="updateDsURL(this.form);">
   <option value="City"selected>City</option>
   <option value="Zip">Postal Code</option>
</select>:
</p>

<div id="mySuggest">
<input type="text" name="radiusValue"/>
<div id="resultsDIV" spry:region="ds1">
<ul>
<li spry:repeat="ds1" spry:suggest="{radiusvalue}">{radiusvalue}</li>
</ul>
</div>
</div>
<script type="text/javascript">
   var theSuggest = new Spry.Widget.AutoSuggest("mySuggest","resultsDIV", "ds1","name",{minCharsType:3, loadFromServer:true, urlParam:'filter'});
</script>

</form>
</body>
</html>

You may ask why two functions? Well, I thought this is something I may want to use again so I made the Spry.Widget.AutoSuggest.addReplaceParams() function fairly generic. For this example I included the function in the inline script block, but if I use it enough I'll probably end up putting it in my AutoSuggest.js file.

OK, that's one down...

ColdFire 1.002

Today Ray and I released ColdFire 1.002 which fixes a few minor issues with JSON encoding. If you were having problems with the variables feature of ColdFire this update may fix your issue. Be sure to update both the Firefox extension and coldfire.cfm debugging template. You can get both via the download at RIAForge.

We have also been discussing upcoming features so if there is something you would really like to see be sure to let us know via the RIAForge site.

Troubleshooting ColdFire

A few people have reported that they are unable to see debugging in the ColdFire Firebug panel. I've been unable to reproduce these issues so I thought I put together a quick post on how ColdFire works in hopes that it may help people debug their issues.

[More]

ColdFire 1.001

Ray just posted version 1.001 of ColdFire over at Riaforge. This version should now work when you open Firebug in a new window.

Get ColdFire!

As you may have seen on Ray's blog, today we released version 1.0 of the ColdFire ColdFusion Debugger. For those that don't know ColdFire is extension to the Firebug Firefox extension, similar to YSlow, only ColdFire displays ColdFusion debugging info. To use ColdFire you need to install a custom debugging template on your server (coldfire.cfm) then configure ColdFusion to use this template via the Administrator. (Detailed installation instructions can be found in the download.)

[More]

ColdFire 0.0.6

On Sunday Ray quietly released version 0.0.6 of ColdFire. With this version, the ColdFire extension actually modifies the user agent by adding "ColdFire/0.0.6" to the end of the string. This allows the coldfire.cfm debug template to check the user agent header for this string and only return debugging info if it is present. This means you shouldn't encounter much debugging overhead if you don't have ColdFire installed or if you choose to disable Firebug for your site. ColdFire only modifies the user agent header if Firebug is enabled.

While this may seem like a minor update it actually involved some major changes to the FireFox extension code. The biggest change was the addition of an XPCOM object to handle the request header modification. Compiling an XPCOM typelib requires the Gecko SDK and unfortunately there isn't an Intel Mac build of the SDK. While I could have tried to build it myself, in the interest of time I ended up switching to my PC for extension development. Even then, building the typelib wasn't exactly straightforward. If you find yourself trying to build a XPCOM component on Windows you may find the following helpful: Find xpidl.exe in Gecko SDK for compiling IDL files.

Anyway , enjoy and look for more ColdFire updates soon.

ColdFire Updates

A while back I sent some updates to the ColdFire ColdFusion debugger over to Ray Camden and today he published those updates. So what's new?

[More]

ajaxCFC and 0 Return Values

I've been using ajaxCFC on a project at work and I ran into an issue today which I thought I'd share.

Say you have a simple ajax component:

<cfcomponent extends="ajax">   
   <cffunction name="echo" output="no" access="private">
      <cfargument name="num" required="yes" type="numeric">      
      <cfreturn val(arguments.num) />
   </cffunction>
</cfcomponent>

You set up a call to this method like so:

<html>
<head>
   <title>echo number example</title>
   <script type='text/javascript'>
      _ajaxConfig = {   '_cfscriptLocation':'echoNumber.cfc',
                  '_jsscriptFolder':'../js',
                  'debug':true};
   </script>
   <script type='text/javascript' src='../js/ajax.js'></script>
   <script type="text/javascript">
      function doEcho()   {
         DWREngine._execute(_ajaxConfig._cfscriptLocation, null, 'echo', 0, doEchoResult);
      }

      // call back function       function doEchoResult (r) {
         alert(r);
      }

   </script>
</head>
<body onload="doEcho();">
   <p>Nothing Happens!</p>
</body>
</html>

This results in the following response:

HTTP/1.1 200 OK
Connection: close
Date: Wed, 07 Mar 2007 18:14:36 GMT
Server: Microsoft-IIS/6.0
Content-Type: text/html; charset=UTF-8


_3952_1173291282046 = 0;

DWREngine._handleResponse('3952_1173291282046', _3952_1173291282046);

This looks fine, however the callback function doEchoResult never gets called. The reason is the DWREngine._handleRespone function checks the result before calling the callback function.

DWREngine._handleResponse = function(id, reply) {
...
if (handlers && reply) {
...
try {
if (handlers.callback) handlers.callback(reply);
}
catch (ex) {
DWREngine._handleMetaDataError(handlers, ex);
}
}
...
}

The problem here is that the reply argument (the _3952_1173291282046 variable in this example) will be evaluated to false so the callback never gets called.

Now the DWREngine code could be updated to handle this by checking that reply != null, however I'm a little leary of changing core framework code. Plus I haven't really taken a good look at the DWREngine code to know if this will cause any other issues. In the end I decided to modify my component function to work around this issue rather than change the core ajaxCFC files. I've submitted a bug to the RIAForge project, so this may get fixed in a future release. (Or maybe Rob Gonda will chime in and tell me I'm doing things all wrong. :)

The Yahoo! User Interface (YUI) Library

Whenever talking about AJAX the conversation always seems to come around to JavaScript libraries for working with the DOM and XMLHttpRequests. Some of the more popular open source libraries are prototype, script.aculo.us and dojo, but lately I've been doing a lot of work with the Yahoo! User Interface (YUI) Library. I chose to use this library on a recent project for several reasons.

First and foremost I needed a tree control, which the YUI has. Actually the library contains several components which Yahoo classifies as either utilities or controls. So far I've been working with the DOM Collection (for DOM scripting) and Connection Manager (for AJAX) utilities as well as the Menu and TreeView controls, but these are just a few of the available components.

Second, Yahoo released the YUI library under the BSD license earlier this year. While I'm no open source licensing expert it's my understanding that the BSD license is one of the more permissive free software licenses, which is great because that means I can use it at work.

Third, the library has excellent documentation, especially when compared to some of the other open source JavaScript libraries out there. Not only are there plenty getting started pages and examples but there is also full API documentation for each of the components. (The documentation is similar to the Java API docs.)

Finally there is a strong community over at ydn-javaScript on Yahoo! Groups. The group appears to be very active and any questions I've had so far working with this library have already been answered on this list.

So, if you are evaluating JavaScript libraries for a future project, be sure to give the YUI a look.

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.