Posted in jQuery, ColdFusion | Posted on 04-12-2010 | 27,197 views
jQuery UI 1.8 has been out for a while now (see details on their blog) but I've yet to really find the time to play around with it. This weekend I finally took a look at it, specifically the new autocomplete widget, and I thought I'd whip up a few quick examples showing the control being used with ColdFusion.
To begin, I recommend taking a look at, and reading some of the docs, for the autocomplete widget. I'm only going to demonstrate a few aspects of it and the docs will give you the whole story. As with most things jQueryUIish (not a word, I know), you use the widget by simply pointing the library at a DOM item and "enabling" it. So for example, once I've got my libraries loaded, I can turn an input control into an autosuggest with one line of code. Obviously I can, and probably will, use more code to get fine grained control over the widget, but it really is pretty simple to use.
Let's start with a super simple example. This one is taken directly from the docs. The only thing I'll point out is - and this bugs me about all the jQuery UI demos, I'm going to show you the full source behind the code. It really bugs me that their View Source content never shows the included JavaScript and CSS commands. Yes, it is implied, but I know I had a hard time with this when I first began to use jQuery UI. When it comes to docs, I think it's always safe to assume less. Ok, mini rant aside. Here is a simple example of a static autocomplete. jQuery UI's autocomplete widget allows for both static, inline auto suggests as well as Ajax loaded content. We'll be looking at an example of that later on.
2<script src="jqueryui/js/jquery-ui-1.8.custom.min.js"></script>
3<link rel="stylesheet" href="jqueryui/css/vader/jquery-ui-1.8.custom.css" type="text/css" />
4
5<script type="text/javascript">
6$(function() {
7 var availableTags = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"];
8 $("#tags").autocomplete({
9 source: availableTags
10 });
11});
12</script>
13
14<input id="tags" />
As you can see, we begin with our includes. We grab the core jQuery library, the jQuery UI library, and the style sheet. For my demos, I've chosen the Vader theme for obvious reasons.
My JavaScript consists of only two real parts. I've got a hard coded list of values in an array. Next, I "enable" the autocomplete on a input control (identified by the ID tags) and tell it to source by my array. And that's it. I really love how easy jQuery UI makes things sometimes. You can test this online
Now let's make this a tiny bit dynamic. In my first ColdFusion version, I'll switch my categories to a ColdFusion variable. (And yes, this is still a static variable, but you can easily imagine it being sourced by the database.)
The first difference here is the cats variable. Again, this would normally come from the database but for now it's just a hard coded set of strings. Going down a bit, take a look at how I translate it to JavaScript. I make use of toScript, a ColdFusion function that translates variables into their relevant JavaScript versions. I first turn the list into an array however. After that, everything else is pretty much the same. You can take a look at this here. (And while there, do a View Source to see how the toScript generated my JavaScript.)
Ok, so while hard coded (or static) variables work, and are ok for small lists, most of the time you will want to load in the data via an Ajax call. The autosuggest widget makes that darn easy. If the value of the source attribute is a string, then jQuery treats it like a URL. In this example, I've pointed my source to a CFC:
Notice that I pass in my method and returnformat. You need to remember that CFCs, by default, return WDDX. Luckily it is easy to get around that (in ColdFusion 8 and higher). The docs did not make it clear, but the name of the argument sent to your server is term. Here is the simple CFC I used.
As you can see, this is nothing more than a simple query. Notice my search string is dynamic on both sides of the term value. This allows me to handle partial matches, so a search for Cold would match both ColdFusion and Ice Cold Baby. If you want to match only results that begin with a term, you simply change how your server side logic works. You can demo this here. The search is against the media table of the cfartgallery demo, so try terms like "Pa" to see a few results.
Ok, so the final demo is pretty cool I think. One of the issues most autocomplete widgets suffer from is that while humans like to work with strings (like "Beer"), the database prefers unique identifiers (like 13). So given that you may return a string, again, "Beer", when you post your form to the server, how do you handle knowing that the value "Beer" referred to row 13 in your database? Typically you need to do another database query. Not a big huge deal, but wouldn't it be nice if your autocomplete could work with both strings and numbers? jQuery UI's autocomplete does this and does it well! Let's begin by modifying our CFC.
In this example I've changed from returning an array of strings to returning an array of structs. Notice that I've got an ID and VALUE key being returned. (*) These values will be recognized by the widget, specifically the value attribute. By itself this won't solve our problem, but we can use the "select" option to handle the user selection event:
This code says - when the user selects an item, grab the ID value and set it to the catid DOM item's value. Let's look at the complete page so it makes more sense.
As you can see, I've added the select handler to my widget constructor. I've also added the hidden form field catid. Finally I added a real submit button and a cfdump so I can see the result. Now when I select a media type, the user will see the nice string, and the hidden form field gets the proper primary key. You can see this for yourself here. All in all I think it works really nicely.
Again - please note there is more to this control then what I've shown here. Check the docs and have fun with it! (And if you are using it in production, please feel free to share the URL here.)
* Did you notice I used struct notation ["id"] instead of dot notation? Dot notation creates JSON with upper case keys. jQuery UI won't pick up on that automatically. By using bracket notation I ensure my JSON maintains the same case.
2
3<script src="jqueryui/js/jquery-1.4.2.min.js"></script>
4<script src="jqueryui/js/jquery-ui-1.8.custom.min.js"></script>
5<link rel="stylesheet" href="jqueryui/css/vader/jquery-ui-1.8.custom.css" type="text/css" />
6
7<script type="text/javascript">
8$(function() {
9 <cfoutput>
10 var #toScript(listToArray(cats),"availableCats")#;
11 </cfoutput>
12 $("#category").autocomplete({
13 source: availableCats
14 });
15});
16</script>
17
18category: <input id="category" />
2<script src="jqueryui/js/jquery-ui-1.8.custom.min.js"></script>
3<link rel="stylesheet" href="jqueryui/css/vader/jquery-ui-1.8.custom.css" type="text/css" />
4
5<script type="text/javascript">
6$(function() {
7 $("#category").autocomplete({
8 source: "service.cfc?method=searchcategories&returnformat=json"
9 });
10});
11</script>
12
13category: <input id="category" />
2
3 remote function searchCategories(string term) {
4 var q = new com.adobe.coldfusion.query();
5 q.setDatasource("cfartgallery");
6 q.setSQL("select mediatype from media where mediatype like :search");
7 q.addParam(name="search",value="%#arguments.term#%",cfsqltype="cf_sql_varchar");
8 var result = q.execute().getResult();
9 return listToArray(valueList(result.mediatype));
10 }
11
12}
2
3 remote function searchCategories(string term) {
4 var q = new com.adobe.coldfusion.query();
5 q.setDatasource("cfartgallery");
6 q.setSQL("select mediaid as id, mediatype as value from media where mediatype like :search");
7 q.addParam(name="search",value="%#arguments.term#%",cfsqltype="cf_sql_varchar");
8 var query = q.execute().getResult();
9 var result = [];
10 for(var i=1; i<=query.recordCount; i++) {
11 result[arrayLen(result)+1] = {};
12 result[arrayLen(result)]["id"] = query.id[i];
13 result[arrayLen(result)]["value"] = query.value[i];
14 }
15 return result;
16 }
17
18}
2 $("#catid").val(ui.item.id)
3}
2<script src="jqueryui/js/jquery-ui-1.8.custom.min.js"></script>
3<link rel="stylesheet" href="jqueryui/css/vader/jquery-ui-1.8.custom.css" type="text/css" />
4
5<script type="text/javascript">
6$(function() {
7 $("#category").autocomplete({
8 source: "service2.cfc?method=searchcategories&returnformat=json",
9 select:function(event,ui) {
10 $("#catid").val(ui.item.id)
11 }
12 });
13});
14</script>
15
16<form action="test3.cfm" method="post">
17category: <input name="category" id="category" />
18<input name="catid" id="catid" type="hidden">
19<input type="submit" value="Submit">
20</form>
21
22<cfif not structIsEmpty(form)>
23 <cfdump var="#form#" label="Form">
24</cfif>


I've just finished writing a magazine tutorial on the revised 1.8 UI, and had great fun playing around with the new features in the library, especially the autocomplete widget.
I love the use of the source param pulling in the data from the web service, and jQuery makes it incredibly easy to pull out the information as you've shown here using the ui.item method.
CF and jQuery go together incredibly well - both rapid development, both extremely extensible, both with a great community.
As to your second point - you would simply change the db query to a cfsearch call instead.
Thanks for the nice little demo Ray.
Secondly - _you_ have ultimate control over the data. I could have easily added a maxrows/top/limit to my query to keep things under control.
I can search for "hotogr" and "Photo", but not "pho".
Is this a jQuery issue? Is there a trick to not having it be case-sensitive? (I use a different jquery autosuggest plugin and was eager to see a demo of this one.)
http://www.jensbits.com/2010/03/18/jquery-ui-autoc...
Not really about autocomplete, but as you said that you had work a lot with JQuery the last time (and jqgrid ?), Could you maybe have an example on how to join a jqgrid cell to a popup window to show data from another table related to the clicked cell (foreign key)
I tried this with the fancybox(http://fancybox.net/home) plugin as follow:
custom formatter to show an icon and to put an id attribute to the 'a' tag as the plugin requires an id attribute. then I should have to link this id attribute to the .fancybox method... but... someone has maybe an idee to accomplish this ?
Greetings,
Michel
Thanks for your reply,
Michel
I made a custom formatter to add an id attribut to the cell data (and btw an icon), and from there
I call a cfm giving the key as parameter to retrieve the data.
And instead of loading this in another page, I try to show this in a fancybox jquery plugin with this:
gridComplete: function(){
$("a[id='userDetails']").fancybox({'type':'ajax'});
}
});
My problem was only to get the userDetails id of the a tag to link it to the fancybox method.
This did nothing: $('#userDetails), but this is ok: $("a[id='userDetails']")
Thank you for your reaction,
Michel
I am newbie to jQuery.Kindly help for the below requirement.
I am having 3 controls say Department, Locations & Name.
In search form,If the user chooses a department with auto suggest & then he comes to location, we need to show all locations of that department in auto suggest and when he comes to Name control,we need to show only names of the persons working in selected values of department & location controls.
Like the way we need to filter, for the user chooses any of the control.
Let me know if you have any questions.
Thanks in advance
I am getting an empty string, when I try to read out a value of a first text box which I am using as a URL param for second text box.
Any clues,code as follows.
$(function() {
$("#department").autocomplete({
source: "test2.cfm"
});
$("#location").autocomplete({
source: "test2.cfm?department="+$("#department").val() //Value in the department text box need to get here & results need to be filtered based on Department
});
});
<form name="frm" action="test.cfm" method="post">
Department: <input id="department" />
Location: <input id="location" />
</form>
*********
$("#department").autocomplete({
source: "test2.cfm?vType=department",
select:function(event,ui) {
var sel = ui.item.value
$("#location").autocomplete("option","source","test2.cfm?vType=location&department="+escape(sel))
}
});
Thanks Ray, its working pretty good now.
a is null
"string"){c=this.options.source;this.s...=this._normalize(a);this._suggest(a);
Any ideas?
Thanks
Mike
Thanks
Keep up the good work :)
Mike
dosnt work
component {
remote function searchCategories(string term) {
var q = new com.adobe.coldfusion.query();
q.setDatasource("cfartgallery");
q.setSQL("select mediatype from media where mediatype like :search");
q.addParam(name="search",value="%#arguments.term#%",cfsqltype="cf_sql_varchar");
var result = q.execute().getResult();
return listToArray(valueList(result.mediatype));
}
}
component {
remote function searchCategories(string term) {
var q = new com.adobe.coldfusion.query();
q.setDatasource("cfartgallery");
q.setSQL("select mediatype from media where mediatype like :search");
q.addParam(name="search",value="%#arguments.term#%",cfsqltype="cf_sql_varchar");
var result = q.execute().getResult();
return listToArray(valueList(result.mediatype));
}
}
should it use
<cfcomponent>
<cffunction>
"the code"
<cffunction>
<cfcomponent>
or could you just share the whole source, please.
Thank u Ray
Thank You Ray
Thanks
-Jay
http://jqueryui.com/demos/autocomplete/#multiple
for(var i=1; i<=query.recordCount; i++) {
result[arrayLen(result)+1] = {
"id" = query.id[i],
"value" = query.value[i]
};
}
Does anything else need to be added to the CFC above?
Confused. (I just name it with the .cfc extension?)
The CFC should be named service2.cfc. You can tell this by the URL used in the last code sample.
Does that make sense?
I'm missing something though - I'm getting a JSON return as I'd expect (according to firebug) - but no suggestion is appearing in the field. Any advice appreciated thanks Ray.
jquery:
$("#category").autocomplete({
source: "getNames.cfc?method=getContacts&returnformat=json"
});
and on the page:
<input type="text" id="category" />
Thanks for the prompt reply.
On the newAppointment page is the text field in question - entering s returns json data Simon and Sam but nothing appears in the field.
Really appreciate the help. Thanks
sorry - I added the site to the website field in the comments box- http://www.yournextvisit.com.au/ynv/newAppointment...
Thanks again Ray, apologies for being a bit of a dolt!
Simon
This is what I tried:
<cfargument name="term" required="false" default=""/>
<cfset var myQuery = "">
<cfset var searchFor = arguments.term>
<cfset result = arrayNew(1)>
<cfquery name="myQuery"
query statements here
</cfquery>
<cfloop query="myQuery">
<cfset result[myQuery.currentRow] = structNew()>
<cfset result[myQuery.currentRow].firstName = myQuery.contactFirstName>
</cfloop>
<cfreturn result>
I'm not sure that the cfloop at the end is doing what I need.
Your data returns like:[{"value":"Painting","id":"1"}]
mine is currently like: [{"FIRSTNAME":"Simon"},{"FIRSTNAME":"Sam"}]
Thanks again for your help, it's really appreciated.
Simon
<cfset result[myQuery.currentRow] = structNew()>
<cfset result[myQuery.currentRow].id = myQuery.ID>
<cfset result[myQuery.currentRow].value = myQuery.value>
and I'm now seeing data that looks more like the data in the example - and I'm getting no errors in firebug. When I enter a value in the filed a container for the autosuggest appears, but still has no content.
I've uploaded the amended files if you want to observe the behavior I'm talking about, and I'm heading over to the jquery doco to revisit again. Thanks for the help (again).
Simon
["id"]
["value"]
That got it Ray - thanks so much (for the patience as well as the help).
Simon
Have you ever seen the page throw an SQL command not properly ended error on the cfc? Here's some of the key debug info:
[Macromedia][Oracle JDBC Driver][Oracle]ORA-00933: SQL command not properly ended
The error occurred in C:\ColdFusion9\CustomTags\com\adobe\coldfusion\base.cfc: line 449
449 : <cfloop index="i" from="2" to="#ArrayLen(sqlArray)#">
And the query looks something like this:
select distinct table_column, table_column_id from db.table where table_column like (param 1) limit 0,15
It's odd that it is doing this. I mean, I barely touched any of the example code. I'm just researching if something like this would work for a page I'm working on.
I know there was a bug with CF9 that could cause this to occur
http://www.thecfguy.com/post.cfm/coldfusion-wrappe...
<cfreturn listToArray(valueList(qSiteAutoCompleteSearch.postTitle)) />
Any idea on how to fix this?
<cfset arr = arrayNew(1)>
<cfloop query="qSiteAutoCompleteSearch">
<cfset arrayAppend(arr, postTItle)>
</cfloop>
<cfsetting showdebugoutput="false">
So I first tried to follow your second example, and made the wsdl result call in a variable, and everything is so OK, but when I try to call www.mydomain.com/cfc/MyWebservice.cfc?method=getEx... directly in the source of the autocomplete, I see in the firefox console that the term variable is appended but the call doesn't show any result. Did I made a wrong call to the wsdl ?
Michel
Ok - that being said, did you look in the FIrebug _console_ or the Network tab? It's a subtle difference and you probably meant console as a way to refer to all of Firebug, but they are separate tools within FIrebug itself. If you are indeed in the Network tab and not seeing anything, maybe scroll down. Don't forget CF can sometimes output a bunch of white space.
It would be best if you could share a public URL so we can see. Or if you want to email me directly, that's fine. (Although I ask that when we find the issue you share the result back here.)
<cfset this.customtagpaths = "(yourroot)\customtags">
to the file. If that doesn't work, well, I'd look at the docs for CFCs in general. It's a big topic, but one you need to pick up on sooner or later anyway. To be honest, the code above could be rewritten in about 5 minutes once you get a hang of the syntax.
http://pastebin.com/Yq68rdR9
<cfset this.customtagpaths = "C:\glassfish3\glassfish\nodes\node1\cf01\applications\cfusion\WEB-INF\cfusion\CustomTags\">
Still not having any luck getting the functionality to work online... locally yes but not on the stupid shared environment.
http://pastebin.com/tGxrJF9z
<cfset var q = new query()>
Make it
<cfset var q = "">
http://code.drewwilson.com/entry/autosuggest-jquer...
:
<cffunction name="getNames" access="remote" returntype="String" >
<cfargument name="search" type="any" required="false" default="">
<cfset var data="">
<cfset var result=ArrayNew(1)>
<cfquery name="data" datasource="dbNAme">
SELECT NAME
FROM myTable
WHERE NAME LIKE '%#trim(ARGUMENTS.search)#%'
ORDER BY NAME
</cfquery>
<cfloop query="data">
<cfset returnStruct = StructNew() />
<cfset returnStruct["label"] = NAME />
<cfset ArrayAppend(result,returnStruct) />
</cfloop>
<cfreturn serializeJSON(result) />
</cffunction>
and im this my cfm:
<script src="jquery-1.4.2.min.js"></script>
<script src="jquery-ui-1.8.custom.min.js"></script>
<link rel="stylesheet" href="jquery-ui-1.8.custom.css" type="text/css" />
<script type="text/javascript">
$(document).ready(function(){
$('#Names').autocomplete(
{source: function(request, response) {
$.ajax({
url: "cfc/getValues.cfc?method=getNames>&returnformat=json",
dataType: "json",
data: {
search: request.term,
maxRows: 10
},
success: function(data) {
response(data);
}
})
},
parse: function(data){
return $.map(data, function(item) {
return { data: item, value: item, result: item };
});
}
});
});
</script>
category: <input id="Names" />
but i can't get to work
<cfreturn serializeJSON(result) />
but you told CF to convert it for you here too:
url: "cfc/getValues.cfc?method=getNames>&returnformat=json"
(I assume the > is a typo, if not, that's a problem)
Basically, you are turning JSON into JSON. Change your cfreturn to be just
<cfreturn result>
Btw, not to sound like a broken record, but if you use Google Chrome Dev Tools or Firebug, you would have seen the result and noticed it was double encoded JSON. I strongly urge folks to use tools like this so you can see what is being returned.
I think I can figure out how to use it based on your examples, but wanted to let you know the demos don't seem to working.
thanks to your post, I'm levitating x wing fighters out of swamps with autocomplete!
cheers
like this :
error: function (xhr, textStatus, errorThrown){
// show error
alert(errorThrown);
}
when i try to write in the textbox i have an alert with undefined value inside. Sorry but it isn't published online .
Unfortunately - if I can't see this myself, I'm limited on how much more I can help.
[Add Comment] [Subscribe to Comments]