Creating dependent dropdowns with Ajax flavour    AJAX

We all know those dropdown lists which are connected to each other. Select a value in one dropdown and the other gets updated according to the previous selection.

This article will explain you the best practice to achieve just that. As a goodie the solution will be fully ajaxed.

Problem summary

Lets assume we have a huge list of articles in our database and want to offer a convenient selection in one of our applications. Providing a simple dropdown which lists all articles would be too common as it would load all the thousands of articles in one long list. What we're going to do is create a second dropdown which allows us to select the articles category first and then load the articles according to its selection.

Solution

We will use the articles of ajaxed.org for this example ;) Here we have a view called active_articles which holds all the current articles and a category table which contains all our categories. Connecting them together is as easy as this..

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub drawCategoryDD()
  with new Dropdown
    .datasource = "SELECT * FROM category ORDER BY catname"
    'we use the category name as a value and as a caption
    .dataTextField = "catname"
    .dataValueField = "catname"
    .name = "cat"
    .commonFieldText = "-- category --"
    'the "no category" item should have a blank value
    'as by default its 0
    .commonFieldValue = ""
    .attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
    .draw()
  end with
end sub

sub pagePart_articlesDD()
  with new Dropdown
    set .datasource = db.getUnlockedRS( _
      "SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
      page.RF("cat") _
    )
    .dataTextField = "title"
    .dataValueField = "id"
    'we disable the articles if we have no selected category
    .disabled = page.RFT("cat") = ""
    .commonFieldText = "-- article --"
    .name = "article"
    .draw()
  end with
end sub

sub main() %>

  <form id="frm">
    <div><% drawCategoryDD() %></div>
    <div id="articles"><% pagePart_articlesDD() %></div>
  </form>

<% end sub %>
Run this code — dropdowns.asp


That was easy. Our code looks nice and everything is done on the server side. What we just did is we created two dropdowns using the Dropdown class and rendered them onto our page. The second dropdown (the one with the articles) has been put into a pagepart (pagePart_articlesDD) so it can easily be called using our ajaxed pagepart technique.

What happens within pagePart_articlesDD is the selection of all articles according to the selection of the category dropdown (see drawCategoryDD procedure). We grab the selected category from the post variables using page.RF("cat") and use it within our where clause. Last but not least we disable the articles dropdown if no category has been selected.

Note: Both dropdowns are using different datasources. The category uses a string and the artciles dropdown uses a recordset. Why? The reason for that is that we want to use a parametrized SQL-query for the articles as we want to prevent SQL-Injection. You could solve that using string concatenation but that would be vulnerable.

In a perfectly normalized database you would rather use an ID for the category and parse the form variable using page.RFP() into a number.

Oh, almost forget the change event. Have a look at the attributes property of our category dropdown. There you find a javascript call to ajaxed.callback() which reload our pagepart (Reminder: as we have a form with the id frm all form fields are passed as post variables to the callback automatically).

Pimp it! Lets give the dropdowns a bit of style

The problem has been solved but I would like to outline some features of the Dropdown class at this point. With only some modifications we are able to do some cool stuff. Lets do the following:

<!--#include virtual="/page.asp"-->
<!--#include virtual="/ajaxed/class_dropdown/dropdown.asp"-->
<%
set page = new AjaxedPage
page.draw()

sub drawCategoryDD()
  with new Dropdown
    .datasource = "SELECT * FROM category ORDER BY catname"
    'we use the category name as a value and as a caption
    .dataTextField = "catname"
    .dataValueField = "catname"
    .name = "cat"
    .commonFieldText = "-- category --"
    'the "no category" item should have a blank value
    'as by default its 0
    .commonFieldValue = ""
    .attributes = "onchange=""ajaxed.callback('articlesDD', 'articles')"""
    'create an event handler to highlight the news item
    .onItemCreated = "onCategory"
    .draw()
  end with
end sub

sub onCategory(it)
  'if the items value is news we make it bold and red
  if it.value = "News" then it.style = "font-weight:bold;color:#f00"
end sub

sub pagePart_articlesDD()
  with new Dropdown
    set .datasource = db.getUnlockedRS( _
      "SELECT * FROM active_articles WHERE category = '{0}' ORDER BY title", _
      page.RF("cat") _
    )
    .dataTextField = "title"
    .dataValueField = "id"
    'we disable the articles if we have no selected category
    .disabled = page.RFT("cat") = ""
    .commonFieldText = "-- article --"
    .name = "article"
    .cssClass = "articles"
    'enable convenient multiple selection
    .multiple = true
    .multipleSelectionType = DD_SELECTIONTYPE_MULTIPLE
    'we need the height if multiple is turned on
    .size = 80
    .draw()
  end with
end sub

sub main() %>

  <style>
   #articleSelection {
     border:2px inset #ccc;
     background:#eee;
     padding:0.5em 1em;
     width:300px;
   }
   .articles {
     overflow:auto;
     white-space:nowrap;
     margin-top:1em;
   }
  </style>

  <form id="frm">
    <div id="articleSelection">
      <div><% drawCategoryDD() %></div>
      <div id="articles"><% pagePart_articlesDD() %></div>
    <div>
  </form>

<% end sub %>
Run this code — dropdown_pimped.asp


Looks not so bad but I am sure some designers could make it better.

Anyway, what we just did is hooked up an event handler for the categories dropdown. That was necessary to style a specific item (in our example the news item). Furthermore we made the articles dropdown appear as a multiple dropdown by turning on its multiple property. Just this change makes the dropdown render as a conventional multiple select tag, but we control the multiple selection with the multipleSelectionType property. This provides the ability to select multiple items using a checkbox and a single item using a radio button. Thats much more user friendly I guess. Play around with it - you will like it.

Last but not least we added some style section to get a appearance. Just have a look at the produced XHTML if you want to style your dropdown. You will get an idea of how to style it quickly.

Tip: You can even create dropdowns with a one-liner. Check the getNew() method for that. You could use it as follows:

Renders a month dropdown with one line:
(new Dropdown)(lib.range(1, 12, 1), "month", month(date())).toString()

Posted in AJAX 5596 days ago

Version 2.1.1 Released3964 days ago