10

I have four entity reference fields to user entity in a form. Host form is node edit form. Three of them are select lists, last is Inline Entity Form.

After adding a user entity via IEF I'm trying to update select lists values:

function ebep_users_feature_users_list_ajax($form, $form_state) {

  $ief = array_shift($form_state['inline_entity_form']);
  $options = array('_node' => t('- None -'));
  $commands = array();

  foreach($ief['entities'] as $data) {
    $options[$data['entity']->uid] = $data['entity']->name;
  }

  foreach(_ebep_users_feature_getUserListFieldNames() as $fieldName) {
    $form[$fieldName]['und']['#options'] = $options;
    $commands[] = ajax_command_replace('#'.$fieldName.'_wrapper', drupal_render($form[$fieldName]));
  }

  return array('#type' => 'ajax', '#commands' => $commands);
}

It works, but when I try to add another user via IEF I am getting:

An illegal choice has been detected. Please contact the site administrator.

5

5 Answers 5

19

--- Original answer. Is wrong, check update below.

Just set

$bla['#validated'] = TRUE

On the select that will be updated with AJAX.

Checkout "Illegal Choice has been detected"

--- Update 17 Dec 2019 ---

Actually that was not good advice, that was a long time ago, now I know better.

Ajax callback function is supposed to only return the piece of form that has changed or some ajax commands.

In this case form modification must be made on the form function or a form alter hook. Form building and validation functions are run each time an ajax callback is called. Just check $form_state on your form build or alter function and adjust values accordingly.

See Resolve the error "An illegal choice has been detected..."

4
  • 1
    If I remember well I tried this, without a success in my case, thanks
    – Codium
    Commented Jan 14, 2014 at 10:49
  • 1
    I'm afraid this doesn't work for me either. Commented Apr 17, 2015 at 10:29
  • Does this not make the form less secure?
    – Binny
    Commented Jan 15, 2019 at 7:27
  • Yes, all previous comments are right. Answer has been updated. Commented Dec 18, 2019 at 4:17
4

I have faced this problem number of times in Drupal 6, changing the values of Select list via Ajax.

Here is what you can do is

  1. If possible, try to insert all the possible values in the in the Field API, you need to edit that field, and then when you change the values, you will have to make sure that only the listed values are in the list.
  2. Solution that I implemented was, you can change the field type from Select list to textfield, from Field API, and ask Drupal to save the value as is. Now, on form runtime, you will have to alter the form, change the type to select list, and push in options, which you want to. While submission as well, you will have to assign the correct value required by the field, that will be saved in DB as is. Make sure your submit handler is called before the default form submit handler, for that you may use array_merge

If first solution works for you, you will save a lot of time coding and testing things. BUT if the list in the drop down is dynamic and you have no control over it, you might have to go with second solution.

Do post if you face any issues, I have done quite lot of work on this problem, for my project, and I did for ~50 forms, PHEW !!! :)

4

I have found the best way round this is to set the #value on the select list.

If we have the value in formstate and it is in our options then we will not get an error. If our value is not in the options then select the first option in our options and use that.

$form['example'] = array(
    '#type' => 'select',
    '#title' => t('Example'),
    '#options' => $options,
    '#value' => isset($form_state['values']['example']) && in_array($form_state['values']['example'],$options)?$form_state['values']['example']:key($options),
  );
2

My solution:

/**
 * Implements hook_form_FORM_ID_alter()
 */
function ebep_users_feature_form_ebep_exhibitor_profile_node_form_alter(&$form, &$form_state, $form_id) {

  foreach(_ebep_users_feature_getUserListFieldNames() as $fieldName) {
    $form[$fieldName]['und']['#prefix'] = '<div id="'.$fieldName . '_wrapper">';
    $form[$fieldName]['und']['#suffix'] = '</div>';
    $form[$fieldName]['und']['#options'] = _ebep_users_feature_getOptionsValuesFromIEFList($form_state);
  }

  $form['#attached']['css'] = array(
    drupal_get_path('module', 'ebep_users_feature') . '/assets/styles.css',
  );

  $form['#attached']['js'] = array(
    drupal_get_path('module', 'ebep_users_feature') . '/assets/scripts.js',
  );

  $form['users_refresh'] = array(
    '#type' => 'submit',
    '#value' => t('Refresh users list'),
    '#ajax' => array(
      'callback' => 'ebep_users_feature_users_list_ajax',
    ),
  );
}

function ebep_users_feature_users_list_ajax($form, $form_state) {

  $commands = array();

  foreach(_ebep_users_feature_getUserListFieldNames() as $fieldName) {
    $form[$fieldName]['und']['#options'] = _ebep_users_feature_getOptionsValuesFromIEFList($form_state);
    $commands[] = ajax_command_replace('#'.$fieldName.'_wrapper', drupal_render($form[$fieldName]));
  }

  return array('#type' => 'ajax', '#commands' => $commands);
}

function _ebep_users_feature_getOptionsValuesFromIEFList($form_state) {
  static $options = array();

  if (empty($options)) {
    $ief = array_shift($form_state['inline_entity_form']);
    $options['_node'] = t('- None -');

    foreach($ief['entities'] as $data) {
      $options[$data['entity']->uid] = $data['entity']->name;
    }
  }

  return $options;
}
-1

In your ajax callback, add:

...

drupal_get_messages();
form_get_errors();

return array('#type' => 'ajax', '#commands' => $commands);

As a security precaution, the core _form_validate() function checks if an element's #options key has changed from what what was set in the original build (which they have). By calling drupal_get_messages() and form_get_errors() we basically tell Drupal to ignore any errors/messages while running our ajax request. :)

2
  • Error message is gone but the select element still contains the error class.How to remove or prevent that? Commented May 25, 2016 at 13:30
  • This is the Drupal equivalent of putting tape over a warning light on your vehicle's dash board. Commented Jan 12, 2018 at 20:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.