- π¨π¦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
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); } }