A few days ago I was looking for a jQuery plugin that would allow me link select lists together, so that selecting an option in one list (the parent) would update the options in the other list (the target). I’d used linkedSelect on a previous project, but for some reason, my ISP kept returning “IP has been banned” each time I tried to browse to the site.
As it turns out, this was a good thing. Eventually, I found this post on Remy Sharp’s blog, and his selectChain plugin was almost exactly what I was looking for. Almost.
There were a few changes I needed to make first:
- Add support for the metadata plugin: since I’ll mostly be using this in my Django projects, being able to specify plugin options declaratively means I can wrap the plugin in a custom widget.
- Add support for caching
- Update target list when the page is loaded, if the parent list has an option selected.
- Preserve the selected option in the target list, if possible (as long as the updated options contain the previous option), when it is updated
- Add a few niceties, such as a preloader GIF :)
To use the plugin, download it and include a reference to it in your HTML page (don’t forget to include a reference to jQuery first!). I’ll assume, you’ve already written the server-side code in your choice framework. If you’d like to be able to customize the plugin behavior declaratively, for specific selects, you’ll also need to download the metadata plugin.
Examples
We’ll use the following HTML:
<form method="post" action="...">
<label for="id_author">Author:</label>
<select name="author" id="id_author">
<option value="1">Enid Blyton</option>
<option value="2">J. K. Rowling</option>
<option value="3">Roald Dahl</option>
</select>
<label for="id_book">Book:</label>
<select name="book" id="id_book">
</select>
</form>
Include the following script before the closing BODY tag, or in your HEAD:
$(function() {
$('#id_book').chainedSelect({
parent: '#id_author',
url: 'http://your.server/app',
value: 'id',
label: 'text'
});
});
That’s it. Your server application should return a JSON-encoded list of objects, having “id” and “text” properties. The “id” will be used as the value of the generated option, the “text” as its label. Selecting an author from the list, should display a list of their books.
If you’re using the metadata plugin, you can move the options into the target select list. Change the HTML for the “book” list to this:
<select name="book" id="id_book" class="chainedSelect {parent: '#id_author', url: 'http://your.server/app', value: 'id', label: 'text'}">
</select>
Then, replace the previous script with the following:
$(function() {
$('.chainedSelect').chainedSelect();
});
Simpler, isn’t it?
Options
Here’s a list of options you can pass, either as metadata, or in the call to “$.chainedSelect”, to customize the plugin:
- parent: A jQuery ID selector, for the parent list – must begin with a ‘#’. Required.
- url: The URL to the server component. The server will be passed a single querystring parameter named “q”, whose value will be the selected option in the parent list. It should return a JSON-encoded list of objects, which will be used to populate the target list. Required.
- value: The property in the objects returned from the server, which should be used as the value for the generated option. Defaults to “pk”
- label: The property in the objects returned from the server, which should be used as the label for the generated option. Defaults to “name”
- type: The HTTP method used to submit the data to the server – defaults to “GET”.
- preloadUrl: A URL to a GIF image, displayed while waiting for an AJAX request to complete. For best results, images should be as small as possible – no larger than 16×16 pixels
- error: Use this to specify a function that will be called if the AJAX request returns an error
Prerequisites
jQuery, of course (tested with 1.2.6)
A server-side component, to generate the JSON used to populate the select lists
Download
jquery.chainedSelect source (6.3KB)
jquery.chainedSelect minified (2.1KB)
If you find this useful, or have any questions, ideas or issues, leave a comment.
can you give a live demo here
I’ll see if I can set something up, and post the link here. Cheers!
I would love to see a demo of this.
Thanks.
Francis.
Hi,
I am trying to use your script but can’t figure out what data it is expecting from the server. For example it expects a length property that you didn’t mention :P
Can you give me a sample of the json returned. I am trying to implement this with Django.
@Bruce: The script expects a list of objects with properties “pk” and “name” (these can be overwritten using the “value” and “label” options)…that’s all. No hidden “length” properties of any kind, I swear :)
Here’s a sample of the JSON being returned in one of the projects I’m using this in:
[ {"pk": 380, "name": "Ado"}, {"pk": 381, "name": "Agatu"}, {"pk": 382, "name": "Apa"}, {"pk": 383, "name": "Buruku"}, {"pk": 384, "name": "Gboko"}, {"pk": 385, "name": "Guma"}, {"pk": 386, "name": "Gwer-East"}, {"pk": 387, "name": "Gwer-West"}, {"pk": 388, "name": "Katsina-Ala"}, {"pk": 389, "name": "Konshisha"}, {"pk": 390, "name": "Kwande"}, {"pk": 392, "name": "Makurdi"}, {"pk": 393, "name": "Obi"}, {"pk": 394, "name": "Ogbadibo"}, {"pk": 399, "name": "Tarka"}, {"pk": 402, "name": "Vandeikya"} ]Here’s the view that does the filtering and returns the JSON:
from django.http import HttpResponse, Http404 from django.utils.encoding import smart_str from django.utils.simplejson import dumps def filter(request, model_class, field_name): try: kwargs = {smart_str(field_name): request.GET['q']} except KeyError: raise Http404 qs = model_class.objects.filter(**kwargs).values('pk', 'name') response = HttpResponse( content=dumps(list(qs)), mimetype='application/json' ) return responseMy urlconf looks something like this:
urlpatterns = patterns('', url(r'^locations/lga/find-by-(?P<field_name>\w+)/$', 'filter', {'model_class': Lga}, name='...') ... )Small tip: I’m using jquery 1.7.2 and I have to change $.attr() to $.prop() when setting/unsetting disabled state of selects lists.
Thank you for this plugin/documentation. I use it in my app; works perfectly.