Reply to comment
Dynamic Dropdowns in Dojo
Submitted by bingomanatee on 22 July, 2009 - 21:59I don't know if I was overthinking it or just unlucky but it took me a while to "get" this very simple and practical way to change one dropdown's option, based on another. The context here was that you choose your state (California, etc.) and your choice of utilities (being based on your state) change too.
Here's the way to do it. Fair warning -- this depends on a web server that feeds options based on the first choice (chosen state). You could proably use static JSON files and choose the right file based on your first choice, but my method relied on a PHP app that fed utilities as a JSON response (list of utilities) back at you.
The dojox widgets used are dojox.form.DropDownSelect and dojox.form.DropDownStack. These elements decorate the standard form select with Dojo look and feel and exensibility.
<style type="text/css"> <!-- @import "/scripts/dijit/themes/tundra/tundra.css"; --> </style> <script type="text/javascript"> //<!-- var djConfig = {"parseOnLoad":true}; //--> </script> <script type="text/javascript" src="/scripts/dojo/dojo.js"></script> <script type="text/javascript"> //<!-- dojo.require("dojox.form.DropDownStack"); dojo.require("dojox.form.DropDownSelect"); dojo.require("dojo.parser"); dojo.require("dijit.form.Form"); dojo.addOnLoad(function() { dojo.parser.parse(); }); //--> </script>
The first step is to create a primary list -- the states
<dt id="utility_state-label"> <label for="utility_state" class="optional">Utility State</label></dt> <dd id="utility_state-element"> <select name="utility_state" id="utility_state" dojoType="dojox.form.DropDownSelect" onChange="update_utilities(this.getValue())"> <option value="AL" label="Alabama">Alabama</option> <option value="AK" label="Alaska">Alaska</option> <option value="AZ" label="Arizona">Arizona</option> <!-- and so on --> </select>
Note the onChange() trigger -- this javascript function is passed the value (currently selected state, as a string) for convenience.
Then the second list. This list is populated by the active selection by default at the server level. Note that the ID's are required to allow the dijit.byId() function to find the widget.
<dt id="utility-label">
<label for="utility" class="optional">Utility</label></dt>
<dd id="utility-element">
<select name="utility" id="utility_select"
dojoType="dojox.form.DropDownStack">
<option value="1" label="OTHER" selected="selected">OTHER</option>
<option value="261" label="Anza Electric Coop Inc">Anza Electric Coop Inc</option>
<option value="262" label="BVE">BVE</option>
<option value="263" label="Banning City of">Banning City of</option>
<option value="264" label="City of Alameda">City of Alameda</option>
<!-- and so on -->
</select>
So now we need two more things: the onChange() script, and the actual list of utilities we'll use to populate the second form. This function is a remote call to my utility server that is passed an object of confiugration that includes a second function, which triggers when the data comes back. Note, the "content" collection includes the state parameter passed from the first function (the selected item -- a two letter code).
<script language="javascript"> function update_utilities(state) { dojo.xhrPost( { url: "/formfill/forms/admin/api/utilities", content: {state: state}, handleAs: "json", load: function(request, ioArgs) { var utils = new Array(); var option; // poll the request and create a list of new options // in the form of a value label pair. for (i in request.utilities) { var o = request.utilities[i]; option = {value: o.utilityid, label: o.uname}; utils.push(option); } // collect all the old option IDs to delete var u = dijit.byId('utility_select'); var oids = new Array(); for (i in u.options) { o = u.options[i]; oids.push(o.value); } // remove the first (everything) ... u.removeOption(oids); // and insert the second. u.addOption(utils); } } ); } </script>
This pattern could be chained as needed, as long as the lower branches are aggressively cleared.
The contribution Dojo makes to this experience include:
- A very clean AJAX transport
- Hooks for adding and deleting options. (why no "clear my options" function? oh well...)
- methods for selecting form widgets.
