Drupal 7 - How to make dynamic select box through Drupal #ajax framework

In Drupal 7 we often need few select boxes to be dependent themselves that means the child select box options are dependent on the parent field's selected option. A very nice example could be Country, State, City those normally has dependency.

Though the trick behind Drupal 7 ajax framework is quite simple but it takes quite a time for a first timer as usual ;) . In this post, I have explained a similar example that does the same with no JS required (Drupal 7 does it behind the screen).

The Example:
I have a Drupal 7 vanilla instance, that has article content type by default. Article content type has 2 select box field depending on each other, Parent field called : Datatype (field_data_type) and child field called as Subdata(field_sub_data).

Apart form the Durpal 7 content type config, I am using a custom module named as 'common' and only a hook_form_alter to modify the form.

So, Lets start by hooking the form :
The form alter:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
/**
 *
 * Implements form_alter();
 * @param type $form
 * @param type $form_state
 * @param type $form_id
 */
function common_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'article_node_form') {

    // Add Ajax callback to parent field.
    $form['field_data_type'][LANGUAGE_NONE]['#ajax'] = array(
        'callback' => 'common_data_type_callback',
        'wrapper' => 'sub-data-container',
    );

    // Add Wrapper div to child field
    $form['field_sub_data']['#prefix'] = '<div id="sub-data-container">';
    $form['field_sub_data']['#suffix'] = '</div>';
}

All this form alter does is, adding an #ajax element to parent field that says Drupal 7 that this field has an ajax callback. which will trigger a function called 'common_data_type_callback' as mentioned in the array.
The next element passed to #ajax array is 'wrapper'. As the name suggests, It searches a DOM that has an id attrib called 'sub-data-container' which we are setting up on our child field(Adding prefix n suffix to child field keeping the id as same as wrapper value in #ajax).

As we are done with writing our form alter we should writing another function as mentioned 'callback'. Here how it should look like :

The ajax callback response:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php
/**
 * Ajax callback function on change of field_data_type
 * This function just returns the replaceable content. all logic written on form_alter.
 * @param type $form
 * @param type $form_state
 * @return type
 */
function common_data_type_callback($form, $form_state) {
  return $form['field_sub_data'];
}
?>

The above callback function only returns back the child field to Drupal when the ajax callback triggers. So, how the options in the child field gets changed. We are not done yet. We have feed the options to second field inside the hook_form_alter with a condition so that the options will be fill in only when there is parent data.

Internally hook_form_alter invokes again even when a ajax callback happens, which is often being called as form gets rebuild.

So below code shows the logic that feeds options to child for each of the parent field data. Just keep it inside the hook_form_alter. It should work fine.

Switching child field options
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
    // When ajax callback comes or form reloads with data in it.
    // Child field should have appropriate options in it.
    if (isset($form_state['values']['field_data_type'][LANGUAGE_NONE][0]['value'])) {
      $subdata_options = array();
      switch ($form_state['values']['field_data_type'][LANGUAGE_NONE][0]['value']) {
        case 'month':
          $subdata_options = array(
              '' => '-- Please Select Month -- ',
              'jan' => 'January',
              'feb' => 'February',
              'mar' => 'Marach',
          );
          break;
        case 'week':
          $subdata_options = array(
              '' => '-- Please Select Day --',
              'sun' => 'Sunday',
              'mon' => 'Monday',
              'tue' => 'Tuesday',
          );
          break;
      }
      $form['field_sub_data'][LANGUAGE_NONE]['#options'] = $subdata_options;
    }
?>

The same procedure should work with other type of fields as well. Hopefully this post helps somebody building a dynamic/dependable fields.

NOTE: If you are copying code from here, make sure you remove the php tags and clear cache after your form alter written.

Comments

Popular posts from this blog

Filtering and Sorting a Doctrine Arraycollection

Doctrine mapping (ORM) with Database (DAL), Unidirectional vs Bidirectional.