jQuery plugin: chained select lists

14 Oct

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:

  1. 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.
  2. Add support for caching
  3. Update target list when the page is loaded, if the parent list has an option selected.
  4. Preserve the selected option in the target list, if possible (as long as the updated options contain the previous option), when it is updated
  5. 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.

About these ads

8 Responses to “jQuery plugin: chained select lists”

  1. Amila January 12, 2010 at 1:54 pm #

    can you give a live demo here

  2. elo80ka January 13, 2010 at 8:59 am #

    I’ll see if I can set something up, and post the link here. Cheers!

  3. Francis Mariani May 11, 2010 at 9:55 pm #

    I would love to see a demo of this.

    Thanks.

    Francis.

  4. Bruce October 3, 2010 at 3:40 am #

    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.

  5. elo80ka October 3, 2010 at 4:33 am #

    @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 response
    

    My urlconf looks something like this:

    urlpatterns = patterns('',
        url(r'^locations/lga/find-by-(?P<field_name>\w+)/$', 'filter', {'model_class': Lga},  name='...')
        ...
    )
    
  6. Cristian September 13, 2012 at 4:57 pm #

    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.

  7. Paul January 28, 2013 at 7:26 pm #

    Thank you for this plugin/documentation. I use it in my app; works perfectly.

  8. Kapone April 24, 2013 at 1:51 pm #

    Line 112: target.attr(‘disabled’, ”).trigger(‘change’);
    should be:
    target.removeAttr(‘disabled’).trigger(‘change’);

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: