Here’s a common problem. You have a form where you want one select box in a form to be dependent on the other. Coldfusion makes this ridiculously easy with its built in AJAX functionality. Let’s take a gander:
For this example, I set up a cfc called “ajax” (clever, eh?). Here is the function inside it…
<cffunction name="getstates" access="remote" returntype="array" hint="lookup states for dropdown"> <cfargument name="country" type="string" required="true" default=""> <!--- Define variables ---> <cfset var data=""> <cfset var i=0> <cfset var result=ArrayNew(2)> <!--- Do search ---> <cfquery datasource="#variables.dsn#" name="data"> SELECT state,postal_code from tblstates where country=<cfqueryparam value="#ucase(arguments.country)#" cfsqltype="cf_sql_varchar"> order by state </cfquery> <!--- Build result array ---> <!--- Convert results to array ---> <cfloop index="i" from="1" to="#data.RecordCount#"> <cfset result[i][1]=data.postal_code[i]> <cfset result[i][2]=data.state[i]> </cfloop> <!--- And return it ---> <cfreturn result> </cffunction>
Mmk, Now let’s look at the actual code on my form page:
<!---here's a query for filling my select box...---> <cfquery name="rscountry" datasource="#application.db#"> SELECT * FROM tblcountries ORDER BY country ASC </cfquery> <!---here's my form---> <cfform method="post" name="form1" action="join.cfm?dealeruname=#this_dealer_username#"> <cfselect name="country" id="country" required="yes" message="Please select your country of residence." query="rscountry" value="country" display="country" selected="United States"></cfselect> <cfselect name="stateprov" bindonload="true" bind="cfc:ajax.getstates({country})" /> </cfform>
That’s it, folks. easy.
But what about when I don’t have a State listing for say… Uganda. Well, we have to have some way of getting the state/province if we don’t have a populated dropdown. Now there’s more to do. There are probably more elegant solutions out there, but this is what I did, and it seems to work pretty well.
Now we’re going to need to use some javascript and a bit more ajax. Let’s do it!
First, let’s get our form prepped:
<!---here's my sexier form---> <cfform method="post" name="form1" action="join.cfm?dealeruname=#this_dealer_username#"> <!---Notice, I've added an onchange event to this field.---> <cfselect name="country" id="country" required="yes" message="Please select your country of residence." query="rscountry" value="country" display="country" selected="United States" onChange="showwhich()"></cfselect> <!---wrap each field in a uniquely identified div---> <div id="dd_state"> <cfselect name="stateprov" bindonload="true" bind="cfc:ajax.getstates({country})" /> </div> <!---add a new text input, and hide it with the div---> <div id="type_state" style="display:none"> <cfinput name="stateprovt" type="text" /> </div> </cfform>
OK, we have our text field. It’s hidden. We’ve got all the other stuff in place, we just need to write the stuff that makes it work!
First, we need to make a new cffunction in our ajax cfc. The function will return a boolean value for whether or not the country has states/provinces within it. You could probably set this up to work with your first function, but I went with a smaller query to reduce some load.
<cffunction name="arestates" access="remote" returntype="boolean" hint="are there states?"> <cfargument name="country" type="string" required="true" default="UNITED STATES"> <!--- Define variable ---> <cfset var data=""> <!--- Do search ---> <cfquery datasource="#variables.dsn#" name="data"> SELECT state from tblstates where country=<cfqueryparam value="#ucase(arguments.country)#" cfsqltype="cf_sql_varchar"> LIMIT 1 </cfquery> <cfif data.recordcount> <cfreturn true> <cfelse> <cfreturn false> </cfif> </cffunction>
Ok, not too bad. Now, let’s bind our page to the ajax cfc so we can use this function. Stick this baby at the top of everything on the page:
<cfajaxproxy cfc="ajax" jsclassname="respond">Now we need to write the javascript to make the magic happen.
function showwhich(){ var r= new respond(); sforc=document.form1.country.value; toshow= r.arestates(sforc); if (toshow == true) { //unhide dropdown, hide text box. document.getElementById("dd_state").style.display='inline', document.getElementById("type_state").style.display='none', document.form1.stateprovt.value="" } else { //hide dropdown, unhide text box. document.getElementById("type_state").style.display='inline', document.getElementById("dd_state").style.display='none', document.form1.stateprovt.value="" } }
You’ll notice I’ve added a line to clear the value of the text field on each change. This is just idiot-proofing so you don’t have someone come in and enter text, then decide they really did live in the US after all. I have further idiot proofed by adding this code before my form processes:
<cfif isdefined("form.stateprov") and TRIM(form.stateprov) neq ''> <cfset use_state=form.stateprov> <cfelseif isdefined("form.stateprovt") and TRIM(form.stateprovt) neq ''> <cfset use_state=form.stateprovt> <cfelse> <cfset runadd="no"> <cfset message=message &"<br />The State/Province you entered is invalid."> </cfif>
Again, I’m sure there are “prettier” ways to accomplish this, but this worked for me!
Tags: AJAX, Coldfusion, Dependent Dropdown, Javascript