Saturday, March 9, 2013

Dynamic loading of select list using AJAX

View updated version of this blog here.

Dynamic loading of select lists in drupal can be easily done using Drupal form API. I had to implement  dynamic select lists in several of my projects, I googled for some direct answer but did not find any. The answer was rather split among several sites or blogs. Hence, I am writing this blog so that you can find all the required code in one single place.

I will create a form using form API where there will be two select lists. The value of the second(lets say it as child list) will be dependent on the selected value of first list(lets say it as parent list). The changing of the value of child list will be dynamic and done using AJAX.

/**
 * @file
 * dynamic_select_list.module.
 */

/**
 * Implements hook_menu().
 */
function blogs_dynamic_list_menu() {
  $items = array();

  // Put your menu items here.
  $items['blogs/dynamic-list'] = array(
    'title' => 'Dynamic lists',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('blogs_dynamic_list_render_select_list'),
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

function blogs_dynamic_list_render_select_list($form, &$form_state) {
  $animal_category = array(
    'none' => t('-- None --'),
    'mammal' => t('Mammal'),
    'reptile' => t('Reptile'),
    'aves' => t('Aves'),
    'amphibia' => t('Amphibia'),
  );

  $mammal = array(
    'tiger' => t('Tiger'),
    'cat' => t('Cat'),
    'zebra' => t('Zebra'),
    'panda' => t('Panda'),
    'squirrel' => t('Squirrel'),
  );

  $reptile = array(
    'snake' => t('Snake'),
    'crocodile' => t('Crocodile'),
    'turtle' => t('Turtle'),
    'lizard' => t('Lizard'),
  );

  $aves = array(
    'parrot' => t('Parrot'),
    'eagle' => t('Eagle'),
    'owl' => t('Owl'),
    'peacock' => t('Peacock'),
  );

  $amphibia = array(
    'frog' => t('Frog'),
    'salamander' => t('Salamander'),
  );

  // Check the value of parent select list and populate the value of child
  // select list according to that.
  $default = !empty($form_state['values']['parent_select_list']) ? $form_state['values']['parent_select_list'] : 'none';

  switch ($default) {
    case 'mammal':
      $child_select_options = $mammal;
      break;

    case 'reptile':
      $child_select_options = $reptile;
      break;

    case 'aves':
      $child_select_options = $aves;
      break;

    case 'amphibia':
      $child_select_options = $amphibia;
      break;

    case 'none':
      $child_select_options = array('none' => '-- None --');
      break;
  }

  // Parent select list.
  $form['parent_select_list'] = array(
    '#type' => 'select',
    '#title' => t('Select animal category'),
    '#options' => $animal_category,
    '#default_value' => $animal_category['none'],
    '#description' => t('Select animal class'),
    '#ajax' => array(
      'callback' => 'blog_dynamic_list_select_list_ajax_callback',
      'wrapper' => 'child-select-list-wrapper',
      'event' => 'change',
    ),
  );

  // Child select list.
  $form['child_select_list'] = array(
    '#prefix' => '<div id="child-select-list-wrapper">',
    '#type' => 'select',
    '#title' => t('Select animal'),
    '#options' => $child_select_options,
    '#description' => t('Select animal'),
    '#suffix' => '</div>',
  );

  return $form;
}

/**
 * AJAX callback.
 */
function blog_dynamic_list_select_list_ajax_callback($form, &$form_state) {
  return $form['child_select_list'];
}

The above code will register a menu item where you can view the form. The form has two select lists, the parent list allows to select a class of animals, and the child list will dynamically show the animals that falls under that class.

I am going to give another example, the following example is more manageable than the previous one. Instead of loading the values of select lists from array, we will load the values from taxonomy.

Create a vocabulary called animals and specify the terms like this:



Now we will load the parent taxonomy terms in the parent select list and their respective child terms in the child select list.
/**
 * @file
 * blogs_dynamic_list_terms.module.
 */

/**
 * Implements hook_menu().
 */
function blogs_dynamic_list_terms_menu() {
  $items = array();

  // Put your menu items here.
  $items['blogs/dynamic-list-terms'] = array(
    'title' => 'Dynamic lists terms',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('blogs_dynamic_list_terms_render_select_list'),
    'access callback' => TRUE,
    'type' => MENU_NORMAL_ITEM,
  );

  return $items;
}

function blogs_dynamic_list_terms_render_select_list($form, &$form_state) {
  // Load the vocabulary. Instead of 'taxonomy_animals' you have to give the
  // machine name of your vocabulary.
  $vocabulary = taxonomy_vocabulary_machine_name_load('taxonomy_animals');

  // Load all the parent terms of the vocabulary.
  // The second parameter 0 means that we wish to generate the whole tree.
  // The third parameter 1 means that we wish to see only the parent terms.
  // Default is 0.
  $parent_terms = taxonomy_get_tree($vocabulary->vid, 0, 1);

  $animal_category = array('none' => '-- None --');
  // Build the parent select list values.
  foreach ($parent_terms as $parent) {
    $animal_category[$parent->tid] = $parent->name;
  }

  // Check the value of parent select list and populate the value of child
  // select list according to that.
  $default = !empty($form_state['values']['parent_select_list']) ? $form_state['values']['parent_select_list'] : 'none';

  if ($default != 'none') {
    // Load all the child terms of the selected parent item.
    // The second parameter tells to load the tree based on the parent term ID.
    // We are ignoring the third parameter since we know that the vocabulary has
    // only two levels.
    $child_terms = taxonomy_get_tree($vocabulary->vid, $default);
    // Build the child select list values.
    foreach ($child_terms as $child) {
      $child_select_options[$child->tid] = $child->name;
    }
  }
  else {
    // No value selected in parent selct list.
    $child_select_options = array('none' => '-- None --');
  }

  // Parent select list.
  $form['parent_select_list'] = array(
    '#type' => 'select',
    '#title' => t('Select animal category'),
    '#options' => $animal_category,
    '#default_value' => $animal_category['none'],
    '#description' => t('Select animal class'),
    '#ajax' => array(
      'callback' => 'blogs_dynamic_list_terms_select_list_ajax_callback',
      'wrapper' => 'child-select-list-wrapper',
      'event' => 'change',
    ),
  );

  // Child select list.
  $form['child_select_list'] = array(
    '#prefix' => '<div id="child-select-list-wrapper">',
    '#type' => 'select',
    '#title' => t('Select animal'),
    '#options' => $child_select_options,
    '#description' => t('Select animal'),
    '#suffix' => '</div>',
  );

  return $form;
}

/**
 * AJAX callback.
 */
function blogs_dynamic_list_terms_select_list_ajax_callback($form, &$form_state) {
  return $form['child_select_list'];
}

See how much the code has shortened. And also it is more manageable, you can easily add or remove parent and child terms in future.
Also you may like to see AJAX Forms in Drupal 7 for more information

3 comments:

  1. Thanks man... your code works... and yes this is only full solution available at one place.... great work :) :)
    can you please help me in more....
    how to work with 3 4 level deep taxonomy terms ?
    and how do i save the taxonomy term references to the node ?
    Thanks in advance..... :)

    ReplyDelete
    Replies
    1. Glad that I am able to help you.

      You just have to increase the number of select lists. Rest of the logic will be same. The select list options will be generated based on selection done in its parent level.

      And you can refer this blog http://timonweb.com/posts/how-to-programmatically-create-nodes-comments-and-taxonomies-in-drupal-7 for saving term references to node.

      Delete
  2. hmm that is realy great snippet for me, i was looking for it badly. thanx a lot :)

    ReplyDelete

I am eager to hear from you