Sunday, March 21, 2010    
Home My Books Blog ColdFusion About Me Back    

Calendar
<< May 2007 >>
S M T W T F S
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    

Search

Categories
 • Acrobat (3) [RSS]
 • Adobe (90) [RSS]
 • AdobeMAX06 (45) [RSS]
 • AdobeMAX07 (59) [RSS]
 • AdobeMAX08 (66) [RSS]
 • AdobeMAX09 (39) [RSS]
 • AdobeMAX10 (1) [RSS]
 • AIR (219) [RSS]
 • Appearances (191) [RSS]
 • Books (72) [RSS]
 • CFEclipse (15) [RSS]
 • ColdFusion (1381) [RSS]
 • Data Services (34) [RSS]
 • Fish Tank (5) [RSS]
 • Flash (197) [RSS]
 • Flex (498) [RSS]
 • Home Automation (5) [RSS]
 • Jobs (116) [RSS]
 • JRun (14) [RSS]
 • Labs (43) [RSS]
 • LiveCycle (34) [RSS]
 • MAX (232) [RSS]
 • Mobile (120) [RSS]
 • Regular Expressions (17) [RSS]
 • RIA (21) [RSS]
 • SQL (40) [RSS]
 • Stuff (536) [RSS]
 • Tips (CF Studio) (80) [RSS]
 • Tips (CF) (795) [RSS]
 • Tips (Dreamweaver) (91) [RSS]
 • Tips (Flex Builder) (2) [RSS]
 • Using CF (162) [RSS]

Other BLOGs
 • Charlie Arehart
 • Lee Brimelow
 • Ray Camden
 • Christophe Coenraets
 • Sean Corfield
 • Mihai Corlan
 • Cornel Creanga
 • Mark Doherty
 • John Dowdell
 • Danny Dura
 • Enrique Duvos
 • Steven Erat
 • Kevin Hoyt
 • Serge Jespers
 • Adam Lehman
 • Duane Nickull
 • Miti Pricope
 • Andrew Shorten
 • Ryan Stewart
 • James Ward
 • Greg Wilson
 • Full As A Goog

RSS Feeds
 • Feed
 • Subscribe

Join my mailing list and find out about new books and other topics of interest.

Thoughts, ideas, tips, musings, and pontifications (not necessarily in that order) by Ben Forta ...
NOTE: This is my personal blog, and the opinions and statements voiced here are my own.

Viewing By Entry / Main
May 31, 2007

ColdFusion Ajax Tutorial 2: Related Selects

Many of us have built related select controls, forms with two (or more) drop down <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.

ColdFusion 8's 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 which uses one of the example databases that comes with ColdFusion. First the ColdFusion Component:

<cfcomponent output="false">

    <cfset THIS.dsn="cfartgallery">

    <!--- Get array of media types --->
    <cffunction name="getMedia" access="remote" returnType="array">
        <!--- Define variables --->
        <cfset var data="">
        <cfset var result=ArrayNew(2)>
        <cfset var i=0>

        <!--- Get data --->
        <cfquery name="data" datasource="#THIS.dsn#">
        SELECT mediaid, mediatype
        FROM media
        ORDER BY mediatype
        </cfquery>

        <!--- Convert results to array --->
        <cfloop index="i" from="1" to="#data.RecordCount#">
            <cfset result[i][1]=data.mediaid[i]>
            <cfset result[i][2]=data.mediatype[i]>
        </cfloop>

        <!--- And return it --->
        <cfreturn result>
    </cffunction>

    <!--- Get art by media type --->
    <cffunction name="getArt" access="remote" returnType="array">
        <cfargument name="mediaid" type="numeric" required="true">

        <!--- Define variables --->
        <cfset var data="">
        <cfset var result=ArrayNew(2)>
        <cfset var i=0>

        <!--- Get data --->
        <cfquery name="data" datasource="#THIS.dsn#">
        SELECT artid, artname
        FROM art
        WHERE mediaid = #ARGUMENTS.mediaid#
        ORDER BY artname
        </cfquery>
    
        <!--- Convert results to array --->
        <cfloop index="i" from="1" to="#data.RecordCount#">
            <cfset result[i][1]=data.artid[i]>
            <cfset result[i][2]=data.artname[i]>
        </cfloop>

        <!--- And return it --->
        <cfreturn result>
    </cffunction>

</cfcomponent>

This CFC contains two methods. getMedia returns all of the media types in the art catalog database, and getArt accepts a media id and returns any art that is associated with that passed id. Both methods convert their results into two dimensional arrays, with the first dimension containing the id (to be used as the value in the <SELECT> control) and the second containing the display text. (For now, this two dimensional array is the format required by <CFSELECT>).

Now for the form itself:

<cfform>

<table>
    <tr>
        <td>Select Media Type:</td>
        <td><cfselect name="mediaid"
                bind="cfc:art.getMedia()"
                bindonload="true" />
</td>
    </tr>
    <tr>
        <td>Select Art:</td>
        <td><cfselect name="artid"
                bind="cfc:art.getArt({mediaid})" />
</td>
    </tr>
</table>

</cfform>

The form contains two <CFSELECT> controls, one named "mediaid" and the other named "artid".

"mediaid" is bound to cfc:art.getMedia(), and so 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 array. As we'd want this control to be automatically populated when the form loads, bindonload is set to "true", this way the getMedia() call is fired automatically at form load time.

"artid" is bound to the getArt method in art.cfc. This method requires that a mediaid be passed to it, and so {mediaid} is used so as to pass the currently selected value of control mediaid (the first <CFSELECT>). 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.

TrackBacks
There are no trackbacks for this entry.

No trackback URL. Trackbacks are only allowed via interactive form.

Comments
Can anyone make this faster when using multiple related cfselects? Not usable with 50 entries in the 2nd cfselect, and progressively gets worse with each additional bound cfselect.
# Posted By Tim V | 6/24/09 11:02 PM
Well, what I did above was able to make it faster. I turned them into multiple=true selects. I have 3 selects, and while they were very slow initially, i had hundreds of entries for the second two depending on the combinations. By doing everything I can to limit the data until those combination are selected it is much faster.
To start, can you default to a generic entry at the top of your list so nothing is loaded until a selection is made?
Also, if you have database queries to get this data, do what you can to speed them up. Dont use SELECT * when you can use SELECT id,name.
Good luck, hope some of this helps.
# Posted By stang89 | 6/25/09 8:38 AM
Thanks for the quick reply; unfortunately does not pertain to my situation. I think the reality is binding multi related cfselects has significant performance issues. Works great for 1sy 2sy related cfselects, but should be avoided with "n" number of select items.
# Posted By Tim V | 6/25/09 9:53 AM
Binding to a url has proven to be much faster... very cool.

<cfselect name="region_name"
bind="url:region_district_store.cfc?method=getRegion&returnFormat=json&region_x=#region#"
bindonload="true" />
<cfselect name="district_name"
bind="url:region_district_store.cfc?method=getDistrict&returnFormat=json&region_x={region_name.value}&district_x=#district#"
bindonload="false" />
<cfselect name="store_name"
bind="url:region_district_store.cfc?method=getStore&returnFormat=json&region_x={region_name.value}&district_x={district_name.value}&store_x=#store#"
bindonload="false" />
# Posted By Tim V | 6/25/09 4:23 PM
@ Tim V...Thank you so much for your post. I've been banging my head for hours trying to speed this up. FF & Safari were running it in less than 2 seconds with my app (which had 4 cfselects using bind) and IE7 took about 40 seconds. Now IE7 takes <10 seconds. You are a life-saver....Now if only you can make Google Maps on IE7 run faster when displaying markers...but that problem is for another forum. Thanks again!
# Posted By Andrew J | 6/27/09 2:32 PM
Tried your art.cfc sample as is but cannot resolve the Error Executing Database Query error.
It seems to be finding my /includes/art.cfc file no problem but can't run the query. I'm stuck without some help.
# Posted By David H | 7/29/09 7:38 PM
Why in component "città" is not well formatted?

How can I set the encoding?
# Posted By Frankie | 8/14/09 3:34 AM
Tim V's comment posted on 6/25/09 is spot on. I was having terrible performance issues with my bind and his example worked like a dream.
# Posted By Christopher Walker | 8/25/09 6:31 AM
I found most of the posting here on CFSELECT is selecting the first select then populate the rest. While I have a situation which I need to populate all the cfselection onload. It seem easy, by setting "bindonload=true",but the hard part is while select anyone of the cfselects and populate the rest. I am pulling my hair but still could not figure out how to do. If only we could manage coldfusion tag "bindonload" in javascript then my job would be easier. So I can use onChange event at the selecting CFSELECT to turnoff the "bindonload" for the rest CFSELECTS. But I javascript does not see it. Could anyone help?
# Posted By Bill | 9/14/09 11:55 AM
I incorporated this into my code back in the beginning of the year and a few days ago it stopped working in IE6 and IE7 - not in firefox though.
I have two related drop down boxes:
<cfselect name="code" bind="cfc:questdata.getcats()" bindonload="true" />
<cfselect name="name" bind="cfc:questdata.getoptions({code})" />
<cfinput type="submit" name="find" value="Find your quest now!">
I have not changed this code since I first put it up there and all of a sudden in IE the boxes are empty and I get this error 'Coldfusion' is undefined.
Has there been some change the to libraries? can someone suggest something? Help!
# Posted By Stacey | 9/14/09 2:47 PM
Thanks for this tutorial Ben.
Do you know if this works with CF7?
# Posted By Dani | 9/24/09 10:09 AM
As the entry says, it is new to ColdFusion 8. You _can_ do related selects in CF7, it just isn't quite as easy. :)
# Posted By Raymond Camden | 9/24/09 10:15 AM
Thanks Ray for your answer (I knew your were going to be the first answer).!!
# Posted By Dani | 9/24/09 10:20 AM
Dani, be advised, if you are using long lists (say over 100 lines) it can be a real bugger to wait for the drop
downs to populate.

I would recommend returning the data a a json object in the bind:
bind="url:#cfc_dirctory#/art.cfc?method=getMedia&returnFormat=json&mediaid=#mediaid#"
it makes the build time on the dropdowns lighting fast.
# Posted By Christopher Walker | 9/24/09 10:41 AM
Thanks for help
# Posted By Mutui Inpdap | 9/24/09 1:53 PM
Hello community, I have another question about this feature.
I have in my form 3 cfselects. The second and third populate depending on whatever has been selected on the first one. So far so good.
This is for my "Add project" screen. Now, if I need to edit the project, I couldn't figure out yet how to select on the cfselects the info from the database but providing still the functionality of the "related" selects. In other words, if I need to modify the information on the second cfselect, it will still show me the options based on what the first cfselect retrieved from the database.
Could someone help me? Ben? Ray? Anybody?

Thanks a lot.

Dani
# Posted By Dani | 10/15/09 2:25 PM
To explain with the code:

<cfselect name="nfgCliID"
bind="cfc:collection.getCompany()"
bindonload="true">
<option selected><cfoutput query="myQuery">#myCompany#</cfoutput></option>
</cfselect>

</td>
# Posted By Dani | 10/15/09 2:28 PM
Try adding cachedwithin="#CreateTimeSpan(0,0,0,0)#" to your cfquery block.
# Posted By Christopher Walker | 10/15/09 2:32 PM
Also if your select looks like:
Select nfgCliID, myCompany

then you shouldn' need to use:
<option selected><cfoutput query="myQuery">#myCompany#</cfoutput></option>
# Posted By Christopher Walker | 10/15/09 2:37 PM
Thank you Christopher, but I think I got lost.
What the cachedwithin will do? Also, in which of my queries should it go? I have the first query to select the company the second to select the contact at that company and the third to select the projects for that company. Thanks for all the help by the way.
# Posted By Dani | 10/15/09 2:51 PM
I only use that exact function when querying tables that are updated frequently.

cachedWithin (optional) Timespan, using the CreateTimeSpan function. If original query date falls within the time span, cached query data is used. CreateTimeSpan defines a period from the present, back. Takes effect only if query caching is enabled in the Administrator.

To use cached data, the current query must use the same SQL statement, data source, query name, user name, and password.
# Posted By Christopher Walker | 10/15/09 4:25 PM
Hi
I have problem of similar kind with the cfselect not working with bind attribute.
The bind attribute is working properly in localhost but when uploaded to server the bind attribute is not working properly. I have application.cfc in my application however i also user this line of code "<cfcontent type="text/html" reset="yes">" at starting inside the function in cfcomponent.
The strange thing which i observed is when commented the cfc completely then I dont see any error dislpaying which is unusal( where an error is expected in normal case). But In my localhost I can see an error as expected.
# Posted By rav | 11/13/09 3:14 PM

  © Copyright 1997-2009 Ben Forta, All Rights Reserved