I have 2 dropdown select lists. The first a kanban process and the second a kanban step. When i select a kanban process the second dropdown should change based on the kanban process. But when i do i get an error: "An illegal choice has been detected. Please contact the site administrator."

<?php
function kanban_form_kanban_task_node_form_alter(&$form, &$form_state, $form_id) {
  
    $options_step = $form['field_kanban_step_ref']['und']['#options'];
    if($form)
    $form['field_kanban_process']['und']['#ajax'] = array(
            'event' => 'change',
            'callback' => 'ajax_dependent_dropdown_callback',
            'wrapper' => 'dropdown-second-replace',
            'method' => 'replace',
    );
   
    $form['field_kanban_step_ref']['und']['#prefix'] = '<div id ="dropdown-second-replace">';
    $form['field_kanban_step_ref']['und']['#suffix'] = '</div>';
    $form['field_kanban_step_ref']['und']['#options'] = $options_step;
   
}

function ajax_dependent_dropdown_callback($form, $form_state) {
    $process = $form['field_kanban_process']['und']['#value'];

    $form['field_kanban_step_ref']['und']['#options'] = get_step_reference($process);
   
    return $form['field_kanban_step_ref'];
   
}

function get_step_reference($kanban) {

$result = db_query('Select n.entity_id FROM {field_data_field_kanban_process_ref} n WHERE n.field_kanban_process_ref_target_id = :field_kanban_process_ref_target_id', array(':field_kanban_process_ref_target_id' => $kanban));
    foreach($result as $record) {
   
         $entity_id[] = $record->entity_id;
    }

    foreach($entity_id as $id) {
        $result2 = db_query('Select t.title FROM {node} t WHERE t.nid = :nid', array(':nid' => $id));

            foreach($result2 as $record2) {
       
                $title[] = $record2->title;
            }
    }
   
    return $title;
}
?>

Comments

Jaypan’s picture

<?php
function ajax_dependent_dropdown_callback($form, $form_state) {
    $process = $form['field_kanban_process']['und']['#value'];

    $form['field_kanban_step_ref']['und']['#options'] = get_step_reference($process);
  
    return $form['field_kanban_step_ref'];
}
?>

Two problems:

1) You are changing the $form element in your ajax callback above. This is causing the error you are seeing. Th only thing you should be doing in this function is returning the part of the form that should be inserted into the existing form. So everything in the above function that isn't part of the return statement should be part of your form definition.

2) You need to render the content that you return, using render().

Think the Drupal forums suck? Add your agreement here: https://www.drupal.org/node/2641072

bostuh’s picture

Could you demonstrate how i could do that?

Jaypan’s picture

If you download the examples module, there is an ajax example in there. That will show you how to do it.

Think the Drupal forums suck? Add your agreement here: https://www.drupal.org/node/2641072

bostuh’s picture

It should be mentioned that this dependent dropdown is going to be used in the function hook_form_alter()

bostuh’s picture

I have looked at the example. But i'm totally frustrated now. Could you please show me how do it?

Jaypan’s picture

It's fine that it's in hook_form_alter(). That doesn't make a difference.

Here's an example:

<?php
function my_form($form, &$form_state)
{
  $form['location'] = array
  (
    '#prefix' => '<div id="location_wrapper">',
    '#suffix' => '</div>',
  );
  $form['location']['country'] = array
  (
    '#type' => 'select',
    '#title' => t('Country'),
    '#options' => array
    (
      '' => '---',
      'canada' => t('Canada'),
      'japan' => t('Japan'),
    ),
    '#ajax' => array
    (
      // Wrapper set in $form['location']
      'wrapper' => 'location_wrapper',
      // Callback function to be shown later
      'callback' => 'my_ajax_callback',
    );
  );
  // The next part will only be entered if a country is selected,
  // ie - during the AJAX callback
  if(isset($form_state['values']['country']) && in_array($form_state['values']['country'], array('canada', 'japan')))
  {
    if($form_state['values']['country'] === 'canada')
    {
      $cities= array
      (
        '' => '---',
        1 => t('Vancouver'),
        2 => t('Montreal'),
      );
    }
    else
    {
     $cities= array
      (
        '' => '---',
        1 => t('Tokyo'),
        2 => t('Yokohama'),
      );
    }
    $form['location']['city'] = array
    (
      '#type' => 'select',
      '#title ' => t('City'),
      '#options' => $cities,
    );
  }
}

// Ajax callback
function my_ajax_callback($form, &$form_state)
{
  return render($form['location']);
}
?>

As you can see, any alterations to the form are done in the form definition, as explained in the comments. In the ajax callback, I have only returned the section to be rendered, and I rendered it using render().

Think the Drupal forums suck? Add your agreement here: https://www.drupal.org/node/2641072

bostuh’s picture

Now it works fine. It populates the right things. But the selected is not saved inside the field_kanban_step_ref. How can i do this?

<?php
function kanban_form_kanban_task_node_form_alter(&$form, &$form_state, $form_id) {
    //$form['field_kanban_process']['#tree'] = true;
    $form['field_kanban_process']['und']['#ajax'] = array(
            'event' => 'change',
            'callback' => 'ajax_dependent_dropdown_callback',
            'wrapper' => 'dropdown-second-replace',
            'method' => 'replace',
    );

    $form['field_kanban_step_ref']['und']['#prefix'] = '<div id ="dropdown-second-replace">';
    $form['field_kanban_step_ref']['und']['#suffix'] = '</div>';
   

    if(isset($form_state['values']['field_kanban_process'])) {
    $process = $form_state['values']['field_kanban_process']['und'][0]['target_id'];

        if(strstr($process, '14')) {
            $options_step =  get_step_reference(14);   
        }
        else if(strstr($process, '39')){
           $options_step = get_step_reference(39);  
        }
        else {
            $options_step = array(
                '' => '- None -',
            );
        }
    $form['field_kanban_step_ref']['und']['#options'] = $options_step;
    }
   
}
function ajax_dependent_dropdown_callback($form, &$form_state) {
    return render($form['field_kanban_step_ref']); 
}
function get_step_reference($kanban) {

    $result = db_query('Select n.entity_id FROM {field_data_field_kanban_process_ref} n WHERE n.field_kanban_process_ref_target_id = :field_kanban_process_ref_target_id', array(':field_kanban_process_ref_target_id' => $kanban));
    foreach($result as $record) {
   
         $entity_id[] = $record->entity_id;
    }
    $title = array();
    $title[''] = '- None -';
    foreach($entity_id as $id) {
        $result2 = db_query('Select t.title FROM {node} t WHERE t.nid = :nid', array(':nid' => $id));

            foreach($result2 as $record2) {
               
                $title[$record2->title] = $record2->title;
            }
    }
    return $title;
}
?>
Jaypan’s picture

It looks like you have set up your ajax properly, which will let the user select a value from the options you give. The next thing you need to do is to attach a submit function to be called after the form is submitted. You can add your submit function to the $form element's #submit array, and it will be called after any existing submit callbacks:

<?php
function my_module_form_alter(&$form, &$form_state, $form_id)
{
  // Rest of form not shown
  if($form_id === 'my_form_id')
  {
     $form['#submit'][] = 'my_submit_function';
  }
}

function my_submit_function($form, &$form_state)
{
  // The value submitted to your #ajax form element
  // is part of the $form_state['values'] array. You can
  // take care of saving it to the database or whatever, here.
}
?>

Think the Drupal forums suck? Add your agreement here: https://www.drupal.org/node/2641072