Posted in ColdFusion | Posted on 09-14-2008 | 6,086 views
Last week I helped a user who was having problems with a multi-related select application. You've probably seen the ColdFusion 8 related selects example before, where one drop down drives the content for another. His code was a bit more complex since he had three drop downs in play instead of two. The main problem he had though was in dealing with the second and third selects when the page loaded. After selecting items in his drop downs everything worked fine, but that initial load kept throwing errors.
Lets take a look at his front end code first:
2<table>
3<tr>
4<td width="100">Division:</td>
5<td width="150">
6<cfselect name="SelDivision" bind="cfc:Places.GetDivision()"
7 display="DsDivision" value="IdDivision" BindOnLoad="true"/></td></tr>
8<tr>
9<td width="100">State:</td>
10<td width="150">
11<cfselect name="SelState" bind="cfc:Places.GetState({SelDivision})"
12 display="DsState" value="IdState" /></td></tr>
13<tr>
14<td width="100">County:</td>
15<td width="150">
16<cfselect name="SelCounty" bind="cfc:Places.GetCounty({SelDivision},{SelState})"
17 display="DsCounty" value="IdCounty"/></td></tr>
18</table>
19</cfform>
Nothing too complex here. The first drop down asks a CFC for a list of divisions. The second drop down is bound to that value. The third drop down is then bound to the first two.
I had him simply debug that arguments being sent to the CFC methods, and as you can probably guess, the drop downs were getting null values for their arguments on the initial load.
To fix this, I recommended that both the getState and getCounty methods should look for empty values being passed. Here is the complete CFC. Notice in the two latter methods how he handles and responds to the empty values.
2
3<cfinclude template="QryPlaces.cfm">
4
5 <cffunction name="GetDivision" access="remote" returnType="query">
6 <cfquery name="LstDivision" dbtype="query">
7 SELECT IdDivision, DsDivision
8 FROM Division
9 ORDER BY DsDivision
10 </cfquery>
11 <cfreturn LstDivision>
12 </cffunction>
13
14 <cffunction name="GetState" access="remote" returnType="query">
15 <cfargument name="Division" type="any" required="true">
16 <cfif ARGUMENTS.Division EQ "">
17 <cfset LstState = QueryNew("IdDivision, IdState, DsState", "Integer, Integer, Varchar")>
18 <cfelse>
19 <cfquery name="LstState" dbtype="query">
20 SELECT IdDivision, IdState, DsState
21 FROM State
22 WHERE IdDivision = #ARGUMENTS.Division#
23 ORDER BY DsState
24 </cfquery>
25 </cfif>
26 <cfreturn LstState>
27 </cffunction>
28
29 <cffunction name="GetCounty" access="remote" returnType="query">
30 <cfargument name="Division" type="any" required="true">
31 <cfargument name="State" type="any" required="true">
32 <cfif ARGUMENTS.Division EQ "" OR ARGUMENTS.State EQ "">
33 <cfset LstCounty = QueryNew("IdDivision, IdState, IdCounty, DsCounty", "Integer, Integer, Integer, Varchar")>
34 <cfelse>
35 <cfquery name="LstCounty" dbtype="query">
36 SELECT IdDivision, IdState, IdCounty, DsCounty
37 FROM County
38 WHERE IdDivision = #ARGUMENTS.Division# AND
39 IdState = #ARGUMENTS.State#
40 ORDER BY DsCounty
41 </cfquery>
42 </cfif>
43 <cfreturn LstCounty>
44 </cffunction>
45
46</cfcomponent>
Yes, he is missing var statements and cfqueryparam tags. So these changes did solve his initial load problem. I'd also probably recommend slimming down his queries a bit. So for example, the third drop down uses dsCount and IdCountry, but his CFC method is returning four columns. Now on one hand, it's nice to be able to use the exact same code for your Ajax code as you would for other code. But if this CFC is only being used for Ajax, he may want to trim down the query for performance reasons. While JSON is used to pass the data to the client and is pretty slim, there is no reason not to make that as small as possible.
Ok, so all in all, a rather simple issue, and I thought folks might like to see this in action. The user, Rigo, was kind enough to take his code and package it up with fake query data. You can download it below, and run a live demo here. Now for the kind of cool part. I noticed when running his demo that the three values for the drop downs were being displayed on the page. He did this with this code:
Note the bind statement. No URL, CFC, or JavaScript function is in use here. All he did was use the bound values. On a whim, I changed it to (and this is what you see in the live demo):
And this worked perfectly fine as well. I had no idea this would work, but I guess if you don't specify URL, CFC, or javascript, than ColdFusion treats it as a literal string result for the div. I'm not sure how I'd use that in production, but for debugging it's pretty useful.


Thanks for the article and the advices. Rigo
<cfselect name="Select1" id="Select1" bind="cfc:GetSelect1Arr()" bindonload="yes" />
<cfdiv name="Select2" id="Select2" bind="cfc:GetSelect2Arr({Select1})" bindonload="no" />
<cfinput type="hidden" name="JSTrigger" bind="javascript:YourJSFunctionHere({Select2.innerHTML})" bindonload="no">
The data displayed are taken from the QryPlaces.cfm file. Check the cfinclude tag at the beginning of the Places.cfc referencing to QryPlaces.cfm
Thanks for the quick response. In Firebug under Console, at the very bottom, I do get the data from the CFC returned:
{"COLUMNS":["IDDIVISION","DSDIVISION"],"DATA":[[1,"Pacific"],[3,"South Atlantic"],[2,"West South
Central"]]}
While I am not new to ColdFuison, I am new to the world of Ajax. So it's entirely possible that I have missed some trivial detail.
I have multiple drop-downs that are populated from same database table, each by selecting distinct record. I first set it as the example in this thread when you select Country, populate State, when select State populate City etc. It works perfect. But the user wants that selection can be started at any drop-down, not like you have to select Country first as in this thread example. The drop-downs are related but they are not always hierarchy. And user want flexibility.
So my trouble is to display drop-downs so user can start selection at any one and populate the rest drop-downs.
I tried to add all arguments to each cfc and make bindonload true, using @change and @none when calling cfcs,
but could not get anywhere. Hope someone could point me to the right direction.
Thanks,
Bill
http://jqueryui.com/demos/sortable/#connect-lists
I just need two selects, one with all available items (minus the ones already chosen) and one with the chosen items. And the ability to move those back and forth, preferably several at a time ;-)
I am glad to find this code, it is very useful.
just in case, how can I do blank first row in the first select if I use a result from the database query ?
Thanks a lot
Have this working well (only using 2 chained selects).
However, I allow users to edit the form - and unfortunately I cannot get the 'selected = "xyz"' to work, so that if they come back to the form its highlighted/selected their previous answer.
Any suggestions?
http://forums.adobe.com/message/3841429#3841429
[Add Comment] [Subscribe to Comments]