Dependent select dropdowns using #ajax in node add/edit form drupal 8 & 9
Suppose you have two drop-down lists, you need to changed in the second value depending on the choice of the first. You need to first add the attribute list #ajax. Attribute #ajax accepts an array as the value. The array contain keys 'callback' and 'wrapper'. Parameter 'callback' may include: array, the first value is object, and the second value - the name of the function of the object.
Example how to create tow dependent select dropdowns using #ajax in node add/edit form drupal 8 & 9:
<?php
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Implements hook_form_alter().
*/
function MODULENAME_form_alter(&$form, FormStateInterface $form_state, $form_id)
{
if ($form_id == 'node_article_form' || $form_id == 'node_article_edit_form') {
//add wrapper to select 2
$form['field_select2']['#prefix'] = '<div id="select2-wrapper">';
$form['field_select2']['#suffix'] = '</div>';
// add ajax to select 1
$form['field_select1']['widget']['#ajax'] = [
'callback' => 'callback_field_select1_trigger',
'wrapper' => 'select2-wrapper',
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => t('Fetching content...'),
],
];
//get select 2 options in edit form
$field_select1_value = $form_state->getValue('field_select1');
if ($form_id == 'node_article_edit_form' && !$field_select1_value) {
$field_select1_edit_value = isset($form['field_select1']['widget']['#default_value'][0]) ? $form['field_select1']['widget']['#default_value'][0] : null;
$form['field_select2']['widget']['#options'] = getSelect2Options($field_select1_edit_value);
}
}
}
/**
* @param array $form
* @param $form_state
* @return mixed
*/
function callback_field_select1_trigger(array $form, $form_state)
{
$field_select1_value = $form_state->getValue('field_select1');
if (!empty($field_select1_value)) {
$select1_value = $field_select1_value[0]['target_id'];
$form['field_select2']['widget']['#options'] = getSelect2Options($select1_value);
} else {
$form['field_select2']['widget']['#options'] = getAllSelect2Options();
}
return $form['field_select2'];
}
/**
* @param $select1_value
* @return array
*/
function getSelect2Options($select1_value)
{
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', "select2_term");
$query->condition('field_select1.0.target_id', $select1_value);
$tids = $query->execute();
$terms = \Drupal\taxonomy\Entity\Term::loadMultiple($tids);
$options = [];
$options['_none'] = t('- Any -');
foreach ($terms as $key => $term) {
$options[$key] = $term->name->value;
}
return $options;
}
/**
* @return array
*/
function getAllSelect2Options()
{
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', "select2_term");
$tids = $query->execute();
$terms = \Drupal\taxonomy\Entity\Term::loadMultiple($tids);
$options = [];
$options['_none'] = t('- Any -');
foreach ($terms as $key => $term) {
$options[$key] = $term->name->value;
}
return $options;
}
Example how to get options if select 1 is multiple field
// example how to get options if select 1 is multiple field
$selectedItems = [];
$default_values = $form['field_select1']['widget']['#default_value'];
if ($default_values != NULL) {
foreach ($default_values as $key => $value) {
if ($value != 0) {
$selectedItems[$value] = $value;
}
}
}
if (count($tab) > 0) {
$form['field_select2']['widget']['#options'] = getSelect2Options($selectedItems);
} else {
$form['field_select2']['widget']['#options'] = getAllSelect2Options();
}
As you can see from this example, the #ajax property for a form element is an array. Here are the details of its elements, all of which are optional:
- callback: The callback to invoke to handle the server side of the Ajax event. More information on callbacks is below in "Setting up a callback to process Ajax".
- wrapper: The HTML 'id' attribute of the area where the content returned by the callback should be placed. Note that callbacks have a choice of returning content or JavaScript commands; 'wrapper' is used for content returns.
- method: The jQuery method for placing the new content (used with 'wrapper'). Valid options are 'replaceWith' (default), 'append', 'prepend', 'before', 'after', or 'html'. See http://api.jquery.com/category/manipulation/ for more information on these methods.
- effect: The jQuery effect to use when placing the new HTML (used with 'wrapper'). Valid options are 'none' (default), 'slide', or 'fade'.
- speed: The effect speed to use (used with 'effect' and 'wrapper'). Valid options are 'slow' (default), 'fast', or the number of milliseconds the effect should run.
- event: The JavaScript event to respond to. This is selected automatically for the type of form element; provide a value to override the default.
- prevent: A JavaScript event to prevent when the event is triggered. For example, if you use event 'mousedown' on a button, you might want to prevent 'click' events from also being triggered.
- progress: An array indicating how to show Ajax processing progress. Can contain one or more of these elements:
- type: Type of indicator: 'throbber' (default) or 'bar'.
- message: Translated message to display.
- url: For a bar progress indicator, URL path for determining progress.
- interval: For a bar progress indicator, how often to update it.
- url: A \Drupal\Core\Url to which to submit the Ajax request. If omitted, defaults to either the same URL as the form or link destination is for someone with JavaScript disabled, or a slightly modified version (e.g., with a query parameter added, removed, or changed) of that URL if necessary to support Drupal's content negotiation. It is recommended to omit this key and use Drupal's content negotiation rather than using substantially different URLs between Ajax and non-Ajax.
Finally you can use our example to create many dependent select dropdowns using #ajax in node add/edit form drupal 8 & 9.
I hope you found this article useful. let me know if you have any questions and I’ll be happy to answer them.