AJAX/Javascript/Coldfusion Dependent Dropdowns with Optional Text Box

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: , , ,

Leave a Reply

Spam protection by WP Captcha-Free