So here is an interesting question. Take a look at Ben's post on related selects in ColdFusion 8 (and don't forget it is even easier now). Pretty simple, right? Well how do you set a control to use Ajax and have a default selected item? Turns out this isn't so easy.
The first thing I tried was selected=, figuring that would be the easiest solution, but unfortunately it didn't work.
I then tried ajaxOnLoad, thinking maybe I could set the default myself. But ajaxOnLoad() is fired when the page is complete, but before those Ajax calls to populate the drop downs are done.
I then tried onLoad in the cfform tag. This didn't work at all. So I threw the problem over to Todd Sharp, and together we were able to come up with a solution. It ain't pretty, but it works. My hope is that folks can look at this and suggest something nicer. I'm actually a bit surprised this isn't supported out of the box. It seems like I use forms half the time to edit content, not create content, so being able to set defaults is a must.
Anyway - the code:
1 <cfajaxproxy bind="javascript:test({mediaid},2)">
2 <head>
3 <script>
4 var imdone = false;
5 function test(x,val) {
6 if(!imdone) {
7 var dd = document.getElementById('mediaid');
8 for(var i = 0; i < dd.length; i++){
9 if(dd.options[i].value == val){
10 dd.selectedIndex = i;
11
12 }
13 }
14 imdone = true;
15 }
16 }
17 </script>
18 </head>
19
20 <cfform >
21 <table>
22 <tr>
23 <td>Select Media Type:</td>
24 <td><cfselect name="mediaid" id="mediaid"
25 bind="cfc:art.getMedia()"
26 bindonload="true" value="mediaid" display="mediatype" /></td>
27 </tr>
28 <tr>
29 <td>Select Art:</td>
30 <td><cfselect name="artid"
31 bind="cfc:art.getArt({mediaid})" value="artid" display="artname" /></td>
32 </tr>
33 </table>
34 </cfform>
So first off - note the use of cfajaxproxy. It is bound to the first drop down. When the value changes, and this occurs on initial load, code is run to set the default. In this case note the hard coded value of 2. This would be #form.selected# or whatever. Also note the use of a variable to remember that the default value has been selected. The cfajaxproxy will always run on change, so we want to be sure it is run only once.
Thoughts? This code only supports one selected item, and only supports defaulting the left control, but obviously it could be extended to handle both. Again though it is a bit disappointing that essentially one line of code:
2 <head>
3 <script>
4 var imdone = false;
5 function test(x,val) {
6 if(!imdone) {
7 var dd = document.getElementById('mediaid');
8 for(var i = 0; i < dd.length; i++){
9 if(dd.options[i].value == val){
10 dd.selectedIndex = i;
11
12 }
13 }
14 imdone = true;
15 }
16 }
17 </script>
18 </head>
19
20 <cfform >
21 <table>
22 <tr>
23 <td>Select Media Type:</td>
24 <td><cfselect name="mediaid" id="mediaid"
25 bind="cfc:art.getMedia()"
26 bindonload="true" value="mediaid" display="mediatype" /></td>
27 </tr>
28 <tr>
29 <td>Select Art:</td>
30 <td><cfselect name="artid"
31 bind="cfc:art.getArt({mediaid})" value="artid" display="artname" /></td>
32 </tr>
33 </table>
34 </cfform>
1 cfselect name="mediaid" id="mediaid" bind="cfc:art.getMedia()" bindonload="true" value="mediaid" display="mediatype" />
had to be extended by about 10 lines of JavaScript. To be fair, my beloved Spry doesn't make this much easier. You can use spry:if type conditionals so it is a bit slimmer. Maybe someone can speak to how other frameworks like Prototype does it?
Edit: A followup post by Todd: Selecting Multiple Default Items With ColdFusion 8 Ajax Controls
Comment 1 written by todd sharp on 7 August 2007, at 2:12 PM
<cfajaxproxy bind="javascript:test({mediaid},'[2,3,4]' )">
<script>
var imdone = false;
function test(x,val) {
if(!imdone) {
var dd = document.getElementById('mediaid');
valArr = ColdFusion.JSON.decode(val);
for(var i = 0; i < dd.length; i++){
//loop over the array of selectedItems
for(var j = 0; j < valArr.length; j++){
if(dd.options[i].value == valArr[j]){
dd.options[i].selected = true;
}
}
}
imdone = true;
}
}
</script>
Comment 2 written by todd sharp on 7 August 2007, at 2:14 PM
Comment 3 written by Sean Coyne on 7 August 2007, at 2:24 PM
Comment 4 written by Raymond Camden on 7 August 2007, at 2:37 PM
Comment 5 written by todd sharp on 7 August 2007, at 2:40 PM
http://cfsilence.com/blog/client/index.cfm/2007/8/...
Comment 6 written by Will on 7 August 2007, at 2:55 PM
Comment 7 written by Mike Benner on 7 August 2007, at 5:25 PM
The docs are here: http://extjs.com/deploy/ext/docs/ for anyone to go through. I looked at them briefly but saw nothing off the top of my head. Will take another look after the kids go down for the night.
Comment 8 written by Eric on 13 September 2007, at 3:17 PM
Comment 9 written by Raymond Camden on 13 September 2007, at 3:21 PM
Comment 10 written by fred on 21 October 2007, at 2:08 PM
I tried updating the javascript by adding the same logic to the 2nd select but it doesn't work.
Thanks.
Comment 11 written by Raymond Camden on 21 October 2007, at 2:15 PM
Comment 12 written by Fred on 21 October 2007, at 2:55 PM
<pre>
<script>
function initSelect(){
var dd2 = document.getElementById('Model');
for(var i = 0; i < dd2.length; i++){
//loop over the array of selectedItems
if(dd2.options[i].value == '<cfoutput>#form.defaultvalue#</cfoutput>'){
dd2.options[i].selected = true;
}
} ;
}
setTimeout("initSelect()",1000)
</script>
</pre>
Comment 13 written by Steve Savage on 2 April 2008, at 4:48 PM
I've created a .js file that "overrides" a function from coldfusion's cfajax.js library.
The new version of the function allows you to specify which options are selected as part of the array you return from your "bound" .cfc function.
See my site for details.
http://www.realitystorm.com/experiments/coldfusion...
Comment 14 written by PhilNg on 11 May 2008, at 9:57 PM
<cfcomponent>
<cffunction name="get" access="remote">
<cfargument name="TABLE_NAME" default="">
<cfargument name="VARIABLE_NAME" default="">
<!--- this is kind of lame, but cold fusion will not set the selected variable!!!!! --->
<cfargument name="SELECTED" default="" type="String">
<cfset answer=ArrayNew(2)>
<!--- this is to set the "selected" item first; so that they're um.. selected. --->
<cfset iter = 2>
<cfset answer[1][1]=selected>
<cfset answer[1][2]=selected>
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfif ucase(trim(data.value[i])) neq ucase(trim(selected))>
<cfset answer[iter][1]=data.value[i]>
<cfset answer[iter][2]=data.display[i]>
<cfset iter = iter + 1>
<cfelse>
<!--- ooo! we have the label of the first selected value! --->
<cfset answer[1][2]=data.display[i]>
</cfif>
</cfloop>
<cfreturn answer>
</cffunction>
Comment 15 written by Mark Chripczuk on 15 July 2008, at 7:02 PM
Comment 16 written by Nando on 26 July 2008, at 11:19 AM
Ray, if you see this comment, how can Steve's solution be passed on to Adobe for possible inclusion in the next update of CF8?
Comment 17 written by Raymond Camden on 28 July 2008, at 7:52 PM
www.adobe.com/go/wish
Comment 18 written by Nando on 29 July 2008, at 7:15 AM
Comment 19 written by George on 1 September 2008, at 11:44 PM
Can you please give us an example how did you work around with Steve Savage's solution? I'm not sure how to use his new function.
Thanks,
George
Comment 20 written by Giedrius on 18 September 2008, at 11:50 AM
I am using the function just for a single cfselect which is using javascript bind.
Comment 21 written by Michael White on 25 September 2008, at 3:26 PM
Comment 22 written by Raymond Camden on 26 September 2008, at 6:43 AM
Comment 23 written by Chris on 23 December 2008, at 6:27 PM
My example uses a product type/category/sub category hierarchy.
I added an argument to each of the functions in the cfc for the ID of the value I wanted to select:
<!--- Get array of product types --->
<cffunction name="getProductTypes" access="remote" returnType="array">
<cfargument name="typeID" type="numeric">
...
<!--- Get category by product type --->
<cffunction name="getCategories" access="remote" returnType="array">
<cfargument name="typeid" type="numeric" required="true">
<cfargument name="catID" type="numeric">
...
<!--- Get sub category by category --->
<cffunction name="getSubCategories" access="remote" returnType="array">
<cfargument name="categoryID" type="numeric" required="true">
<cfargument name="subCatID" type="numeric">
...
In each of the functions I added a cfif to the query to array loop to set the new array element to true for the passed ID:
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#getTypes.RecordCount#">
<cfset result[i][1]=getTypes.typeID[i]>
<cfset result[i][2]=getTypes.type[i]>
<cfif getTypes.typeID[i] is arguments.typeID>
<cfset result[i][3]=true>
</cfif>
</cfloop>
...
(repeat for categories and sub categories)
Then on the display page I included Steve's magic JavaScript and added the arguments to each of the cfselect tags:
<cfselect name="typeID" bind="cfc:typeCatSubCat.getProductTypes(#typeID#)" bindonload="true" />
...
<cfselect name="catID" bind="cfc:typeCatSubCat.getCategories({typeID},#catID#)" />
...
<cfselect name="subCatID" bind="cfc:typeCatSubCat.getSubCategories({catID},#subCatID#)" />
Comment 24 written by Frankie on 18 February 2009, at 6:56 PM
you have used also Steve script?
or you have made this works only with cfc?
I cannot understand how I can make it works...
Comment 25 written by Frankie on 22 February 2009, at 10:27 AM
this is my code:
<cfcomponent output="false">
<!--- Get array of media types --->
<cffunction name="getRegioni" access="remote" returnType="array" output="no">
<cfargument name="regioneid" type="numeric">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="#application.dsn#">
SELECT fld_id,fld_provincia,fld_id_regione
FROM tbl_province
ORDER BY fld_id
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.fld_id[i]>
<cfset result[i][2]=data.fld_provincia[i]>
<cfif data.fld_id[i] is arguments.regioneid>
<cfset result[i][3]=true>
</cfif>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get art by media type --->
<cffunction name="getProvince2" access="remote" returnType="array" output="no">
<cfargument name="regione" type="numeric" required="true">
<cfargument name="provinceid" type="numeric">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="#application.dsn#">
SELECT fld_id, fld_localita
FROM tbl_localita
WHERE fld_id_provincia = #ARGUMENTS.provinceid#
ORDER BY tbl_localita.fld_localita
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.fld_id[i]>
<cfset result[i][2]=data.fld_localita[i]>
<cfif data.fld_id[i] is arguments.provinceid>
<cfset result[i][3]=true>
</cfif>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
</cfcomponent>
And this is my form:
<script src="http://www.agriturismoincampania.it/prove/fixColdf...;
<cfform>
<cfselect name="regione" bind="cfc:select2.getRegioni(1)" bindonload="true" id="regione" />
<cfselect name="fld_id_localita" id="fld_id_localita" bind="cfc:select2.getProvince2({regione},20)" />
</cfform>
Where is my error?
Comment 26 written by Raymond Camden on 22 February 2009, at 11:49 AM
Comment 27 written by Frankie on 22 February 2009, at 1:02 PM
bind failed, element not found: regioneid.
Any idea?
Comment 28 written by Raymond Camden on 22 February 2009, at 1:07 PM
Comment 29 written by Frankie on 22 February 2009, at 1:18 PM
I have uploaded my code in:
http://www.agriturismoincampania.it/prove/select2....
and the form
http://www.agriturismoincampania.it/prove/sel.txt
Where is the error? could you help me,please?
Comment 30 written by Raymond Camden on 22 February 2009, at 1:20 PM
Comment 31 written by Raymond Camden on 22 February 2009, at 1:20 PM
Comment 32 written by Frankie on 22 February 2009, at 1:28 PM
I really don't understand where is the error...
I will go crazy!!
Comment 33 written by Raymond Camden on 22 February 2009, at 1:29 PM
Comment 34 written by Frankie on 22 February 2009, at 1:32 PM
http://www.agriturismoincampania.it/prove/sel.cfm
It's possible my error is how I import the javascript fixColdfusionAjax.js?
Comment 35 written by Frankie on 22 February 2009, at 1:36 PM
window:global: Error loading script (http://www.agriturismoincampania.it/prove/fixColdf...=, line 1)
Comment 36 written by Raymond Camden on 22 February 2009, at 2:34 PM
Also, what is fixColdFusionAjax.js?
Comment 37 written by Frankie on 22 February 2009, at 3:39 PM
I've simplified my page....I have red the example in the comment of Chris...
Comment 38 written by Raymond Camden on 22 February 2009, at 4:03 PM
<script src="http://www.agriturismoincampania.it/prove/fixColdf...;
Comment 39 written by Frankie on 22 February 2009, at 4:42 PM
But I think there is another problem, because the second select doesn't retrive any value....
I don't think I will make it work....
Comment 40 written by Frankie on 22 February 2009, at 4:48 PM
error:http: Error invoking CFC /CMS/select2.cfc : The REGIONEID argument passed to the getProvince2 function is not of type numeric.
Probably because if I type http://www.agriturismoincampania.it/select2.cfc?me... it give me the value with true on the selected value....
I need to change my cfc?
Sorry if I stress you with many question but in Italy there aren't many coldfusion's developers...
Comment 41 written by Raymond Camden on 22 February 2009, at 5:00 PM
Comment 42 written by Frankie on 22 February 2009, at 5:08 PM
Comment 43 written by Raymond Camden on 22 February 2009, at 5:18 PM
Comment 44 written by Frankie on 22 February 2009, at 5:26 PM
Finally I made it works....
Thank you for your support...
Comment 45 written by Raymond Camden on 22 February 2009, at 5:40 PM
Comment 46 written by Frankie on 11 March 2009, at 6:43 PM
Two or more....
Comment 47 written by Sheldon Goldberg on 7 April 2009, at 6:45 PM
you call getValues?
Is there any code to show this being implemented?
Any help would be really appreciated.
Comment 48 written by dan fredericks on 6 July 2009, at 10:25 AM
<cfselect name="courseType" id="courseType" bind="cfc:cfcs.template.getCourseType()" value="courseMethodID" display="courseMethodDescription" bindOnLoad="true" tooltip="Course Type"> <option value="0">Select Type</option></cfselect>
On my initial screen, the option will not show up...first problem.
When the user submits that page back to itself, I need to value they selected to be autoselected on the page again...need a summary page they can change before submiting to db. I tried the JS solution on this page, however, I received an error stating my courseMethodID value was undefined.
<cfajaxproxy bind="javascript:test({courseType},#form.courseType#)">
<script language="javascript">
var imdone = false;
function test(x,val) {
if(!imdone) {
var dd = document.getElementById('courseType');
for(var i = 0; i < dd.length; i++){
if(dd.options[i].value == val){
dd.selectedIndex = i;
}
}
imdone = true;
}
}
</script>
Below is the source of my file where the error is located:
<script type="text/javascript">
_cf_bind_init_1246893138308=function()
{
ColdFusion.Bind.register([['courseType','','change']],{'bindTo':ColdFusion.empty,'bindToAttr':'true','callFunction':'test','bindExpr':[['courseType','','value'],ILT]},ColdFusion.Bind.jsBindHandler,false);
};ColdFusion.Event.registerOnLoad(_cf_bind_init_1246893138308);
</script>
any help would be appreciated
Comment 49 written by dan fredericks on 6 July 2009, at 2:26 PM
<cfajaxproxy bind="javascript:test({courseType},#form.courseType#)">
it won't work right. Once I figured out I was not using numeric, and switched, it works just fine.
thanks for the code.
Comment 50 written by Francesco on 16 July 2009, at 8:25 PM
I've made this related select, but I have problem with charset. In fact one of my value has accent but I cannot make it formatted properly....
Could someone help me?
This is the link to the component output:
http://www.agriturismoincampania.it/select.cfc?method=getLocalita&provincia=2" target="_blank">http://www.agriturismoincampania.it/select.cfc?met...
And this is on site: http://www.agriturismoincampania.it
What I have to do to make this work? I have tried to put cfheader on utf-8 but nothing change...
Comment 51 written by Steve Savage on 17 July 2009, at 8:32 AM
http://www.w3schools.com/tags/ref_entities.asp
Comment 52 written by Anthony on 3 September 2009, at 12:29 AM
Comment 53 written by Sean Sekora on 9 December 2009, at 12:16 PM
Comment 54 written by Alex Moustris on 5 February 2010, at 5:39 AM
<cfselect bind="cfc:hotelier.components.GeoInfo.getRegionsByCountry({CountryID})" enabled="No" bindonload="yes" name="RegionID" class="FormFields" id="RegionID" multiple="no" value="RegionID" display="RegionStringEN" selected="#HotelDetails.RegionID#">
<cfoutput> <option value="#HotelDetails.RegionID#" selected="selected">#HotelDetails.REGIONSTRINGEN#</option></cfoutput> ---></cfselect>
[Add Comment] [Subscribe to Comments]