Tuesday, December 29, 2009

jQuery Autocomplete and ColdFusion

While migrating my applications from Prototype to jQuery, I needed to replace the Ajax.Autocompleter that comes with script.aculo.us, the effects library written for Prototype.

After a quick Google search, I found a pretty good plugin that looked like it would do what I wanted. However, there was a pretty big difference in how the two plugins work. Ajax.Autocompleter must return an unordered list (<ul>), with each suggestion rendered inside its own list item (<li>). Piece of cake.

However, the jQuery Autocompleter expects a string, with each suggestion rendered on its own line. This threw me for a loop at first, since ColdFusion isn't always the best at dealing with whitespace. After a little thought, here's what I came up with.

index.cfm

<link rel="stylesheet" type="text/css" href="jquery.autocomplete.css" />
<script src="jquery-1.3.2.js"></script>
<script src="jquery.autocomplete.js"></script>

<script type="text/javascript">
$().ready(function() {
$('#search').autocomplete('UserService.cfc?method=autocomplete&_cf_nodebug=true', {
multiple: true,
formatItem: function(row) {
var user = JSON.parse(row.toString());
return user.name;
},
formatResult: function(row) {
var user = JSON.parse(row.toString());
return user.email;
}
});

$('#search').result(function(event, data, formatted) {
var user = JSON.parse(data.toString());
alert('You selected user '+user.id);
});

});
</script>

<input type="text" id="search" name="search" size="60" />


UserService.cfc

component {

remote void function autocomplete(required string q) {

// get the users
var users = searchUsers(arguments.q);
var result = [];
var i = "";

for (i=1; i <= arrayLen(users); i++) {

// build the user record
var user = {};

// maintain lowercase keys
user["id"] = users[i].id;
user["name"] = users[i].name & " (" & users[i].email & ")";
user["email"] = users[i].email;

// serialize the user record and append it to the result
arrayAppend(result, serializeJSON(user));

}

// convert the result from an array to a list, with each user on its own line
var html = arrayToList(result, chr(10));

// output the JSON result
writeOutput(html);

}

public array function searchUsers(required string search) {

// normally this would query the db,
// but for this demo I'll just create a static array of users
var users = [
{id="1", name="Adam", email="adam@email.com"},
{id="2", name="Bob", email="bob@email.com"},
{id="3", name="Brady", email="brady@email.com"},
{id="4", name="John", email="john@email.com"},
{id="5", name="Kaitlyn", email="kaitlyn@email.com"},
{id="6", name="Leanne", email="leanne@email.com"},
{id="7", name="Lisa", email="lisa@email.com"},
{id="8", name="Mike", email="mike@email.com"},
{id="9", name="Nate", email="nate@email.com"},
{id="10", name="Ryan", email="ryan@email.com"},
{id="11", name="Sean", email="sean@email.com"},
{id="12", name="Tony", email="tony@email.com"},
{id="13", name="Tyler", email="tyler@email.com"}
];

return users;

}

}


On a side note, JSON.parse() is a built-in function only available to newer browsers, like Firefox 3.5 and IE 8.0. Does anybody know of a good plugin to accommodate older browsers? Something similar to Prototype's String.evalJSON would be preferred.

6 comments:

  1. You could use ColdFusion.JSON.decode to parse the JSON string - check the docs:

    http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=JavaScriptFcns_07a.html

    ReplyDelete
  2. cool other people actually know of _cf_nodebug=true, I wish more people used it

    ReplyDelete
  3. @Todd: I never thought of that. I'd prefer to keep my JavaScript code server-language agnostic, but I don't imagine I'll be moving away from ColdFusion anytime soon, so that should work just fine.

    @Josh: I only know of it because of you, so don't get too excited. Also, it feels a little hacky to me for some reason, but I don't know why.

    ReplyDelete
  4. The place to find all things JSON is http://json.org There's a create/proces JSON for almost every language, including JavaScript. You should also check out jQuery, which is excellent at providing a consistent API despite browser differences.

    ReplyDelete
  5. Durr, sorry, I didn't have enough coffee obviously, because you have "jQuery" all over your post. To be more helpful, based on listening to jQuery podcasts with the creators and heavy users, json2 is a highly preferred solution: JSON in JavaScript

    ReplyDelete
  6. @Tony, Yeah I found json2 as well and it seems to be what I want. However, when I was testing it with my current code set I was running into issues with converting strings to JSON. Apparently Prototype can evaluate JSON strings with keys that are wrapped in single quotes ({'name': 'Tony'}) or double quotes ({"name": "Tony"}), but json2 only supports double quotes. I'm not saying Prototype is right, but just that I had problems.

    A simple solution was to update JSON.parse to use eval("("+text+")") rather than eval('('+text')'), which was a lot easier than updating all of my code to make sure all of my JSON strings used double quotes.

    ReplyDelete