Ask a Jedi: Mixing ColdFusion 8 binding with jQuery

This post is more than 2 years old.

Josh asks:

I have been trying to do this for a little while and haven't found any good answers online. Is there any way that you know of to use jQuery to prepend an option to the top of a cfselect that has a bind=some cfc?

I haven't done a lot of mixing of ColdFusion 8 and jQuery, but let's see what we can do with this. First, let's start with the code he tried to use:

var Options = { "" : "View All"
}

$("#views").change(
    function(){
        $(this).addOption(Options,true)
    })</code>

This looked simple enough, but when I tried to turn it into a full demo, the first thing I ran into was an error. Turns out that 'addOption' isn't core jQuery, but rather a plugin. As a gentle nudge to my readers, or to anyone blogging about jQuery, it may be a good idea to mention when a code sample uses a plugin. In this case, he was using this plugin: ::TexoTela:: jQuery - Select box manipulation. As you can guess, it adds a few new utility methods to drop downs, including the ability to easily add an option. I was then able to generate a complete demo to replicate his issue:

test2.cfm, my main client side file:
<script src="jquery/jquery.js"></script> <script src="jquery/jquery.selectboxes.js"></script> <script> var Options = { "" : "View All" } $(document).ready(function() { $("#views").change(function(){$(this).addOption(Options,true);});
} ); </script>

<cfform name="foo"> <cfselect bind="url:test.cfm" id="views" name="views" bindOnLoad="true" display="state" value="id"> </cfselect> </cfform>

Note the use of binding in the select to test.cfm. Above this is his JavaScript code, bound to the change function. test.cfm isn't too important, but here is the code so you get on the same page:

<cfset q = queryNew("id,state")> <cfset queryAddRow(q, 2)> <cfset q["id"][1] = 1> <cfset q["state"][1] = "Louisiana"> <cfset q["id"][2] = 2> <cfset q["state"][2] = "Virginia">

<cfset d = serializeJSON(q)> <cfcontent type="application/json" reset="true"><cfoutput>#d#</cfoutput>

Ok, so what happens when you run this demo? Since his code is bound to the change event, on initial load you see the two states, but if you switch from Louisiana to Virginia, the third option is added. What he really wanted was "When CF8 is done doing it's Ajax crap, run my stuff." Unfortunately, as far as I know, this is not possible. I don't think there is a DOM event for 'something was added to a drop down', and even if there was, we wouldn't want to run on every addition, but only after the last addition.

I tried the ColdFusion 8 ajaxOnLoad function, but this runs immediately after the page loads and before the Ajax call is made.

I went with the assumption that you really wanted to use the bind attribute, and with that, the only way I could see getting this to work was to switch to a bind to a javaScript function. This is what I came up with, and it's not too pretty:

<script src="jquery/jquery.js"></script> <script src="jquery/jquery.selectboxes.js"></script> <script> var d = ""; function getData() { var result = new Array(); $.ajaxSetup({async:false});
$.getJSON('test.cfm',{}, function(d) {
	for(var i=0; i &lt; d.DATA.length; i++) {
		var id = d.DATA[i][0];
		var label = d.DATA[i][1];
		item = new Array();
		item[0] = id;
		item[1] = label;
		result[result.length] = item;
	}
} );
//hard coded option 
var newitem = new Array();
newitem[0]  = "";
newitem[1] = "View All";
result[result.length] = newitem;

return result;	

} </script>

<cfform name="foo"> <cfselect bind="javascript:getData()" id="views" name="views" bindOnLoad="true" display="state" value="id"> </cfselect> </cfform>

The bind now runs getData. The problem we have here though is that we must return data from this function. To do this, I had to tell jQuery to use synchronous Ajax calls. This is the ajaxSetup call. (As far as I know, this is the only way to do asynchronous calls with the next line.) Next we run getJSON and call our existing script. ColdFusion returns the data within a DATA key, which is a 2D array of items where element 0 is the ID and element 1 is the name. Finally, I added a hard coded option to the end. I could have added this to the front of the array as well.

I don't know about you, but that is a heck of a lot of code. I went ahead and made another demo. This time the assumption was - continue to use test.cfm for our data provider, but get rid of the bind requirement. Ie, do it all jQuery based. Please keep in mind I'm a jQuery newbie, but I think this is a bit nicer:

<script src="jquery/jquery.js"></script> <script src="jquery/jquery.selectboxes.js"></script> <script> var Options = { "" : "View All" }

$(document).ready(function() { $.getJSON('test.cfm', {}, function(d) { for(var i=0; i<d.DATA.length; i++) { $("#views").addOption(d.DATA[i][0], d.DATA[i][1]); } }); //hard coded option $("#views").addOption(Options, true); } )

</script>

<form name="foo"> <select id="views"></select> </form>

I've switched from cfform to a simple form and now make use of the select plugin to add each option as I loop over my JSON data. It is also now completely asynchronous which makes me all warm and fuzzy inside.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Josh posted on 12/16/2008 at 8:04 PM

I ended up just doing an ajax call out to a page that would have just the select box on it and the call for the query. That way I could pass in the variable from the one select box and still have my "View All" option. I still wish their was a better way to set it up just using cf8.

Comment 2 by todd sharp posted on 12/16/2008 at 8:04 PM

You might have tried rolling your own ajaxproxy to populate the select and then added the option with jQuery in the callback function. But I think at this point I'd just switch out the CF ajax for pure jQuery or at the very least just add the item on the server side.

Comment 3 by Josh posted on 12/16/2008 at 8:49 PM

Another option I know that would be easy but I could never get it to work right. Would be to do a Query Add Row with the "View all" option in it.

Comment 4 by Justin Carter posted on 12/17/2008 at 1:29 AM

Josh, instead of QueryAddRow() you would probably have to use a Query of a Query with a SQL UNION statement to append / prepend your static row to the rest of the query.

Comment 5 by Kate posted on 2/13/2009 at 10:56 PM

I believe I have fully copied your example, but I must have done something wrong. I only get the "View All" option, and niether of the states.

Eventually I'd like to get this problem solved: http://www.adobe.com/cfusio... and was directed that this would be a good place to start. Any ideas on why I would not be getting subsequent options?

Comment 6 by Raymond Camden posted on 2/13/2009 at 11:25 PM

What does Firebug tell you? Is the Ajax request fired?

Comment 7 by Kate posted on 2/17/2009 at 12:30 AM

Firebug says there's an exception, but I honestly have no idea what to do about it. I don't think the Ajax request is getting fired because AJAX Logger never updates when I change options, and no values for the second cfselect ever show up in the AJAX Logger either.

Firebug error:
[Exception... "'SyntaxError: parseJSON' when calling method: [nsIDOMEventListener::handleEvent]" nsresult: "0x8057001c (NS_ERROR_XPC_JS_THREW_JS_OBJECT)" location: "JS frame :: chrome://firebug/content/spy.js :: onHTTPSpyReadyStateChange :: line 483" data: no]
http://www.myurl.com/CFIDE/...
Line 103

Comment 8 by Miked posted on 2/20/2009 at 4:01 AM

Ray,

I have a Cf8 form that has tabs on it. Within one of the tabs i am trying to get the jquey tablesorter() plugin to work. I have this plugin successfully working on all of my other pages, however it does not work when it is within the cflayoutarea. Is there some special syntax in need to use in this situation?

I have tried adding the javascript
$(document).ready(function() { $("#act").tablesorter (); } );

on the main page and in the cflayout tab. neither place works. Also the code for the cflayout area is called with the source attribute on the cflayoutarea tab.

Thanks, Mike

Comment 9 by Raymond Camden posted on 2/20/2009 at 6:59 AM

Instead of document.ready, try using ajaxOnLoad. See CFML Ref for syntax.

Comment 10 by Miked posted on 2/20/2009 at 7:45 PM

Ray,

That worked great, thanks for the help

Mike

Comment 11 by Niall posted on 3/9/2009 at 2:00 PM

Hi Ray,
I had a similar issue where I wanted to prepend a row in my combo options list with "Please select...".

I wasn't bound to using jQuery to solving this - so instead my solution was to use the MS SQL UNION Statement to select a blank row, of the same column select list, before my actual query:

<code>
SELECT 0 AS userID, 'Please select...' AS userForename, ' ' AS userSurname, ' ' AS userEmailAddress
FROM tblUsers

UNION

SELECT userID, userForename, userSurname, userEmailAddress
FROM tblUsers
INNER JOIN tblUserGroupRlnshps ON tblUsers.userID = tblUserGroupRlnshps.userGroupRlnshpsUserID
WHERE tblUsers.userIsEnabled = 1
etc... etc...
</code>

This way - the database is doing all the work and I don't need to manipulate my result set in ColdFusion.

Thanks,
Niall.

Comment 12 by Ron West posted on 3/17/2009 at 8:21 PM

I am not sure if this appropriate here but I will try anyways. I am calling a function in a cfc with the returnformat="json" using getJSON. I continue to get an "invalid label" error in Firebug. As a note I am doing the getJSON call to a different subdomain:

jQuery("document").ready( function(){
jQuery.getJSON("https://new.american.edu#request.site.csAppsWebURL#pt_profile/com/utils.cfc?jsoncallback=?", { method: "getProfileData", userID: "rwest" },
function(_data){
// reset the link

});
});

Comment 13 by Raymond Camden posted on 3/17/2009 at 8:25 PM

Is your method returning json data?

Comment 14 by Ron West posted on 3/17/2009 at 8:42 PM

My function deceleration is:

<cffunction name="getProfileData" access="remote" returntype="struct" returnformat="json">

Comment 15 by Raymond Camden posted on 3/17/2009 at 8:46 PM

ok, so if in your callback you do console.dir(_data), do you see what you think you should see?

Comment 16 by Ron West posted on 3/17/2009 at 8:49 PM

I think the error is occurring prior to the function call the error. However in firebug under the error is the data structure as I would expect it.

Comment 17 by Ron West posted on 3/17/2009 at 8:53 PM

Also, as a note - I am using this same function call in other places successfully.

The main difference here is that I am using a subdomain (e.g. jQuery.getJSON() call lives on "cms.american.edu" and the URL to the CFC in the getJSON() function is "new.american.edu".

I read online that the getJSON function allows you to make Ajax calls to other domains.

Comment 18 by Raymond Camden posted on 3/17/2009 at 8:57 PM

Before the call? You got me then. Your JS looks VERY slim there so I can't imagine where else it would fail.

Comment 19 by Raymond Camden posted on 3/17/2009 at 8:57 PM

Oh wait! I see the issue. More details coming in a sec.

Comment 20 by Raymond Camden posted on 3/17/2009 at 9:00 PM

So yes, jQuery lets you make JSONP calls to things on other servers, but this is NOT the same as a JSON call.

A JSONP call is a JSON result with "padding". Please see these two articles and it should help a lot:

http://www.insideria.com/20...

This article talks about JSONP.

http://www.coldfusionjedi.c...

This article demonstrates making a JSONP service in ColdFusion.

Comment 21 by Ron West posted on 3/17/2009 at 9:15 PM

Thanks Ray, that makes sense.