Login

Dependent Lists and Ajax Form Submission

In this post we going to demo how simple it is to create dynamic forms with Drupal 7 Form API (aka FAPI).

Our form will have 2 select lists (aka drop down combo boxes). Our first list lets a user select a US State. Once a state has been selected, we dynamically render a "city" select list. The city select list only contain cities for the selected state.

We will include a submit button, however the form will be submitted asynchronously (aka via AJAX). 

Upon submission, we will asynchronously render a short message. The message tells the user which city they just submitted.

Here is our form in it's initial state:

Here is our form after a user selects a state:

Finally, here is our form after the submit button is clicked:

Now our code. Let's start with the form definition. We are creating a separate select list for Illinois and a separate select list for Ohio. We add a '#states' array directive to both the Illinois and Ohio lists. The '#states' array directive controls whether a list is displayed or hidden depending on what value is selected in the parent list (state_list). "':input[name="state_list"]" is a JQuery expression.

You should also note the directives we added to our submit button element. We explicitly turn off the normal synchronous submit (i.e. full page repaint) by using "'#executes_submit_callback' => FALSE". Instead we submit asynchronously with '#ajax' => array('callback' => 'advanced_form_callback', 'wrapper' => 'status',)'.

Our form definition:

function advanced_form($form, &$form_state) {
 
  $form['status'] = array(
    '#type' => 'markup',
    '#prefix' => '<div id="status">',
    '#suffix' => '</div>',
    '#markup' => '',
  );
 
  $form['state_list'] = array(
      '#type' => 'select',
      '#title' => 'Choose A US State First',
      '#options' => array(0 => 'Choose', 1 => 'Illinois', 2 => 'Ohio'),
      '#default_value' => 0,
  );
 
  $form['illinois_cities'] = array(
      '#type' => 'select',
      '#description' => t("Cities in Illinois."),
      '#states' => array(
          'visible' => array(
              ':input[name="state_list"]' => array('value' => '1'),
          ),
      ),
      '#options' => array(0 => 'Chicago', 1 => 'Evanston', 2 => 'Springfield'),
      '#default_value' => 0,
  );
 
  $form['ohio_cities'] = array(
      '#type' => 'select',
      '#description' => t("Cities in Ohio."),
      '#states' => array(
          'visible' => array(
              ':input[name="state_list"]' => array('value' => '2'),
          ),
      ),
      '#options' => array(0 => 'Cleveland', 1 => 'Akron', 2 => 'Grantsville'),
      '#default_value' => 0,
  );
 
  $form['submit'] = array(
      '#type' => 'submit',
      '#value' => 'Submit',
      '#executes_submit_callback' => FALSE,
       '#ajax' => array(
          'callback' => 'advanced_form_callback',
          'wrapper' => 'status',
           ),
 
  );
 
  return $form;
}

Now our callback. We simply inspect what the user selected and return the name of the city. I added a little helper function called "form_result_helper" to navigate through the $form_state structure (source below).

function advanced_form_callback($form, &$form_state) {
  $city_name = form_result_helper($form_state);
  $element = $form['status'];
  $element['#markup'] = t('You submitted @city', array('@city' => $city_name));
  return $element;
}

Our helper function:

function form_result_helper($form_state) {
  $us_state = $form_state['values']['state_list'];
  $city_name = t('No City Selected');
  $city = '';
  switch ($us_state) {
    case 1 :
       $city = $form_state['values']['illinois_cities'];
       switch($city) {
          case 0:
            $city_name = 'Chicago';
          break;
          case 1:
            $city_name = 'Evanston';
          break;
          case 2:
            $city_name = 'Springfield';
         break;
       }
    break;
    case 2 :
      $city = $form_state['values']['ohio_cities'];
       switch ($city) {
        case 0:
          $city_name = 'Cleveland';
          break;
        case 1:
          $city_name = 'Akron';
          break;
        case 2:
          $city_name = 'Grantsville';
          break;
      }
    break;
  }
  return $city_name;
}

That's it. Two dependent drop down lists, and a Ajax submission. Drupal 7 makes it really easy.