share
Drupal AnswersDynamic select list in the form (dependent dropdown)
[+5] [4] Ben
[2011-08-28 10:08:16]
[ 7 ajax forms ]
[ http://drupal.stackexchange.com/questions/10112/dynamic-select-list-in-the-form-dependent-dropdown ]

I'm using Drupal seven. I want to make the options in a select list be dependent on the value chosen in another select list in a form. I'm sure this has been asked many times before, but I am having difficulty finding a clear answer for how to do this.

The form is for users to enter a work history. They need to select a squadron which is a node reference to the squadron field type, and this is in a drop-down list. However, the squadron, is dependent upon a city drop-down list. Users first need to select a city which will then filter the options for the squadron. In the squadron content type, I created a taxonomy for city which gets tagged to the squadron.

I would be very grateful for any pointers as to the best way (simplest?) to go about this, or for any useful resources online which would help.

Many thanks in advance,

Ben

[+9] [2011-08-28 11:25:26] jordojuice [ACCEPTED]

You can use Ajax to accomplish this. Drupal 7 has good Ajax support now. On your first select list (city) you'll need to add Ajax information. Then, the second select list can be populated based on the information in the first. You can also even hide the second select list until an option in the first is selected, and I'll explain how to do that in a bit. First, to set up the basic form:

$form['city'] = array(
  '#type' => 'select',
  '#title' => t('City'),
  '#options' => $options,
  '#ajax' => array(
    'event' => 'change',
    'wrapper' => 'squadron-wrapper',
    'callback' => 'mymodule_ajax_callback',
    'method' => 'replace',
  ),
);
$form['squadron_wrapper'] = array('#prefix' => '<div class="squadron-wrapper">', '#suffix' => '</div>');
$form['squadron_wrapper']['squadron'] = array(
  '#type' => 'select',
  '#title' => t('Squadron'),
  '#options' => $squadron_options,
);

This is just the basic setup of the elements. Now you'll need a way to determine what options should go in squadron. First you need to make your Ajax callback identified in 'city' select list. In most cases you can just return the element that wraps the ajax element, in this case $form.

function mymodule_ajax_callback($form, $form_state) {
  return $form;
}

Now, when the 'city' select list changes it will rebuild the squadron-wrapper part of the form. Your 'city' value will now be in $form_state['values']. So, when the form is rebuilt we need to determine what options to give to the select list based on the value of 'city'.

// Get the value of the 'city' field.
$city = isset($form_state['values']['city']) ? $form_state['values']['city'] : 'default';
switch ($city) {
  case 'default':
    // Set default options.
    break;
  case 'losangeles':
    // Set up $squadron_options for los angeles.
    break;
}

// If you want to hide the squadron select list until a city is
// selected then you can do another conditional.
if ($city !== 'default') {
  $form['squadron_wrapper']['squadron'] = array(
    '#type' => 'select',
    '#title' => t('Squadron'),
    '#options' => $squadron_options,
  );
}

(3) Examples can be found in the Examples module ("AJAX Examples" → "Dependent dropdown"). You can also see at Hierarchical Select module. - kalabro
By the way, alternatively you can do this in a multi-step form, but I didn't think that sounded like what you were looking for. Also ^ good call! Examples modules are great for learning this kind of thing. - jordojuice
@jordojuice Thank you very much for your answer. I am working on it now. In the third example of the code you give above (beginning //Get the value...) which function do I put this part of the code in? Does it go in the _ajax_callback function? Thanks - Ben
I followed the example module for this but i got an error everytime i select an item in the first drop down:Warning: array_values() expects parameter 1 to be array, string given in _field_filter_items() (line 525 of I:\My Documents\web\xampp\htdocs\mysite\modules\field\field.module). I'm using a multi step form in conjunction with this ajax dependent drop down that i wrote in a custom override module.... Although the values get changed for the second dd based on the first. It's just a warning showing up though but irritating... can someone please help me remove that warning? thanks! - jan
1
[+6] [2011-08-29 10:32:27] Ben

Many thanks to jordojuice above. With his help I managed to find a solution. I also refered to the example at http://public-action.org/content/drupal-7-form-api-dependent-lists-and-ajax-form-submission. I eventually used the code below which worked in a custom module. For some reason I couldn't find any of my values in the $form_state values, but was able to find them in $form. Finally, when I tested, I was getting an error message that Drupal had detected an illegal choice in the drop-down. I got round this by commenting out line 1290 in form.inc:

form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));

The final code I used was:

<?php

function sappers_squadron_form_work_history_node_form_alter(&$form, &$form_state) {     
        //echo '<pre>';
        //print_r ($form);
        //echo '</pre>';

        $squadron_options = array();

        if(isset($form['field_wkhist_city']['und']['#default_value'][0])) {
            $city = $form['field_wkhist_city']['und']['#default_value'][0];
        }
        else {
            $city = 0;
        }

        $squadron_options = sappers_squadron_squadrons($city);

        $form['field_wkhist_city']['und']['#ajax'] = array(
            'event' => 'change',
            'wrapper' => 'squadron-wrapper',
            'callback' => 'sappers_squadron_ajax_callback',
            'method' => 'replace',
        );

        $form['field_squadron']['und']['#prefix'] = '<div id="squadron-wrapper">';
        $form['field_squadron']['und']['#suffix'] = '</div>';
        $form['field_squadron']['und']['#options'] = $squadron_options;
}


function sappers_squadron_ajax_callback($form, $form_state) {   
    $city = $form['field_wkhist_city']['und']['#value'];

    $form['field_squadron']['und']['#options'] = sappers_squadron_squadrons($city);

    return $form['field_squadron'];
}


function sappers_squadron_squadrons($city) {
    $nodes = array();

    $select = db_query("SELECT node.title AS node_title, node.nid AS nid FROM  {node} node INNER JOIN {taxonomy_index} taxonomy_index ON node.nid = taxonomy_index.nid WHERE (( (node.status = '1') AND (node.type IN  ('squadron')) AND (taxonomy_index.tid = $city) )) ORDER BY node_title ASC");

    $nodes[]="";

    foreach ($select as $node) {
            $nodes[$node->nid] = $node->node_title;
    }

    return $nodes;
}

?>

2
[+1] [2011-12-01 14:49:27] Siripong

The root cause of "An illegal choice has been detected. Please contact the site administrator." is that the empty string with the value of 0 added by $nodes[]=""; is invalid for the field_squadron field.

See Advance PHP Programming and Development [1], but keep in mind that the DANGEROUS_SKIP_CHECK and validated flags are deprecated in D7 [2].

After I removed that line, the error was gone.

[1] http://raviverma.info/resolved-drupal-error-an-illegal-choice-has-been-detected-please-contact-the-site-administrator/
[2] http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7

3
[0] [2011-11-23 07:06:00] Hacker

put the line of code i.e
$nodes[''] = '- None -'; after

 $nodes = array();

in ur sappers_squadron_squadrons function and that will solve your error

form_error($elements, $t('An illegal choice has been detected. Please contact the site administrator.'));


4