Altering the #options property of a checkboxes field in an ajax callback function doesn't reflect the changes.

Created on 25 August 2014, over 10 years ago
Updated 17 May 2024, 7 months ago

Problem/Motivation

When is used the ajax api for alter the property #options of a field with the type "checkboxes", the changes aren't reflected.

Steps to reproduce it:

A field with an #ajax property, and a field with type checkboxes.

$form['target'] = [
      '#type' => 'select',
      '#title' => t('Test'),
      '#options' => ["option1" => "option 1", "option2" => "option 2"],
      '#ajax' => [
        'callback' => [$this, 'updateCheckboxes'],
        'wrapper' => 'edit-options',
        'method' => 'replace',
        'event' => 'change',
      ],
];

 $form['test'] = [
      '#type' => 'checkboxes',
      '#title' => $this->t('Options'),
      '#options' => ["test1" => "Test 1", "test2" => "Test2],
      '#prefix' => '<div id="edit-options">',
      '#suffix' => '</div>',
    ];

The callback for modify the #options property

  public function updateCheckboxes($form, FormStateInterface $form_state) {
    $form['test']['#options'] = ["test3" => "Test 3", "test4" => "Test4"];
    return $form['test'];
  }

When is triggered the javascript event the result will be the old values.

Internally the checkboxes type field render the #options list into fields with the type "checkbox" and when the ajax callback return the field the #options property is ignored and render old values.

πŸ› Bug report
Status

Active

Version

11.0 πŸ”₯

Component
AjaxΒ  β†’

Last updated 1 day ago

Created by

πŸ‡²πŸ‡½Mexico gnuget Puebla

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡¨πŸ‡¦Canada ryanrobinson_wlu

    I'm having a similar problem except that it is a dropdown select instead of checkboxes. I have one select field that has an AJAX callback which restricts the allowed options of another dropdown field. It will work once, showing the message that it is running and then updating the values of the second field. But then if I try to change the first field again, it will show the message that it is running and afterward the options of the second field have not changed at all.

    I tried adding a couple of the options mentioned above that did not help:

    \Drupal::formBuilder()->doBuildForm($form['#form_id'], $field, $form_state);

    and

    \Drupal\Core\Render\Element\Select::processSelect($element,$form_state,$form);

    Here's some more context of the code:

    class ReservoBookingForm extends ContentEntityForm {
    
      /** AJAX fired function to determine allowed values of end time */
      public function updateEndTimes(array &$form, FormStateInterface $form_state) {
        $element = $form['booking_end_time'];
        $options = [different complicated function to get valid options];
        $element['widget']['#options'] = $options;
        $element['widget']['#empty_option'] = [different function that returns different text whether there are any remaining options or not]
        /* Note: empty option requires patch for a bug discussed here: https://www.drupal.org/project/drupal/issues/3180011 */ 
        return $element;
    
      }
    
      public function buildForm(array $form, FormStateInterface $form_state) {
    
        $form['booking_start_time']['widget']['#ajax'] = [
          'wrapper' => 'edit-booking-end-time-wrapper',
          'callback' => [$this, 'updateEndTimes'],
          'event' => 'change',
          'progress' => [
            'type' => 'throbber',
            'message' => $this->t('Checking available end times'),
          ],
        ];
    
        return $form;
    
      }
    
    }
    
  • πŸ‡ΊπŸ‡ΈUnited States mrweiner
  • πŸ‡ΊπŸ‡ΈUnited States mrweiner

    Just a note that I was able to solve this issue in my node_form_alter with the following:

    function my_form_alter(&$form, $form_state_interface) {
        $ajax_wrapper_id = 'date-ajax-wrapper';
        $form['#prefix'] = "<div id=$ajax_wrapper_id>";
        $form['#suffix'] = '</div>';
    
        // Add an AJAX callback to the date field.
        $form['field_dates']['widget'][0]['value']['#ajax'] = [
          'callback' => 'my_callback',
          'event' => 'change',
          'wrapper' => $ajax_wrapper_id,
        ];
    
    }
    
    function my_callback() {
        $form['my_field']['widget']['#options'] = ['a', 'b'];
        foreach (Element::children($form['my_field']['widget']) as $i) {
          unset($form['my_field']['widget'][$i]);
        }
        Checkboxes::processCheckboxes($form['my_field']['widget'], $form_state, $form);
        return $form;
    }
    

    No need to fuss with ajax responses and whatnot. Just wrap the entire form, return the entire form, and ensure the checkbox field is totally updated. The problem is that updating '#options' alone is not enough. All of the children checkbox elements need to be replaced as well.

  • πŸ‡ΊπŸ‡ΈUnited States mrweiner

    Alright, so I actually did the same thing as above but just in buildForm() instead of in the ajax callback. That worked for getting the form to work as expected in general.

    Since it's an existing entity form, I needed to override it with my own implementation to be able to change the buildForm behavior, like so

    function my_module_entity_type_alter(array &$entity_types) {
      $entity_types['node']->setFormClass('default', \Drupal\my_module\Form\NodeForm::class);
      $entity_types['node']->setFormClass('edit', \Drupal\my_module\Form\NodeForm::class);
    }
    

    Then I needed to add custom validate callback to grab the value from the form and set it as the value.

    function my_form_alter(&$form, $form_state_interface) {
        $ajax_wrapper_id = 'date-ajax-wrapper';
        $form['my_field']['widget']['#prefix'] = "<div id=$ajax_wrapper_id>";
        $form['my_field']['widget']['#suffix'] = '</div>';
    
        // Add an AJAX callback to the date field.
        $form['field_dates']['widget'][0]['value']['#ajax'] = [
          'callback' => 'my_callback',
          'event' => 'change',
          'wrapper' => $ajax_wrapper_id,
        ];
    
        // Make sure to unshift so it runs before the default validation,
        // or submission will fail due to null value for the submitted field. 
        array_unshift($form['#validate'], 'my_callback');
    
    }
    
    function my_callback(&$form, FormStateInterface $form_state) {
        $vals = $form_state->getUserInput()['my_field'];
        if ($vals) {
          // Logic to get/modify your values as needed
          $form_state->setValue('my_field', $vals);
        }
      }
    
Production build 0.71.5 2024