After setting up Chained Select Boxes (formerly known as the Universally Related Popup Menu or URPM) for many clients, I like to think that I've become fairly knowledgeable on the subject. In theory, they are not all that complicated: HTML Select elements whose contents change based on the selection of a parent control, such as Country - Province/State - City or Vehicle Manufacturer - Model - Trim Level.
Of course, the devil's always in the details, and getting them to behave how you want using the HTML Select form element is not a trivial thing. Luckily, since the emergence of JSON, object literals, and JavaScript frameworks like Dojo, setting up Chained Select Boxes (CSBs) on your Web page does not have to be a hair pulling ordeal. For the basics on CSBs, see my Create Chained Select Boxes with the Dojo Libraries article. Once you've done that, you'll be ready to explore additional possibilities, including how to decouple the setting of the child's contents from the parent list's onChange()
event handler, setting default items, clearing the previous selection, and more.
Exploring Additional Data Store Options
Dojo's Select box controls are dojo.data
-enabled. This means rather than embedding all the <OPTION>
tags within the page, you can have dojo.data
fetch them from a server-based store. These can include flat files, databases, and Web services. As long as the data is formatted as a JavaScript Object Literal (a slightly less strict cousin of JSON), you can easily configure your Select boxes to work with them. Fetching the data dynamically is best performed in the dojo.addOnLoad()
event although you could do it during widget creation if you're declaring them programmatically.
The following code shows how to hook up the data to a Select box. First, a local variable is created to store the object literal. That variable is then passed to the ItemFileReadStore
constructor via the data
attribute. Hence, the data
parameters expects an Object literal while url
should point to a file. A standard setter method assigns the new data store to the controls' store
property:
Registering the Child Lists as Listeners
onChange()
event handler was housed in the parent list. There, a forEach()
loop iterated through the listeners
array and executed the same code for each:
That implementation is fine for simple cases, but it does not accommodate listeners who want to perform a different action in response to the event. In Java, the event listener model dictates that each listener should implement its own event handling code. Typically, this is done using an Interface which implements a common method such as actionPerformed()
. Of course, JavaScript does not support interfaces, but by using Dojo's programmatic creation style, you can bind the child to the parent's onChange()
event using the dojo.connect()
method. It accepts four arguments:
- the owner of the event (the parent control)
- the event name ("onChange")
- the
this
pointer - the handler function
As demonstrated in the following code, the best time to register a widget as a listener is post creation:
Passing an unnamed (anonymous) function to dojo.connect()
works just fine, but take care to pass along the this
pointer to the dojo.connect()
method so that it binds (hitches in Dojo) the child list to the event handler function. Otherwise, this
will reference the state list when it executes.
All that we need to include in the HTML is the input tag with an id
attribute.
Using the listener model allows us to include a generic onChange()
handler in the parent, while encapsulating each child's own code within the object. Better yet, we can accomplish everything that what we want without having to subclass the FilteringSelect control since no extra properties are required.