The class documentation of InsertCommand and all commands extending it only state that they accept HTML as input content, while InsertCommand::construct() correctly states that it accepts a render array or an HTML string. But passing HTML creates problems with AJAX forms.
The problem is that the documentation erroneously push developers to convert render arrays into HTML, with \Drupal::service('renderer')->render()
, before passing the output to the AJAX command. It often happens with Form API elements. Any element which had '#ajax' properties set will lose them if they are rendered as HTML before being passed to one of these AJAX commands.
If instead of passing HTML, the render array was passed directly to one of these AJAX commands, the '#ajax' properties will work as expected.
This kind of issue can be hard to debug since the documentation seems to indicate we have to use HTML with these AJAX commands.
Affected:
- AfterCommand
- AppendCommand
- BeforeCommand
- HtmlCommand
- InsertCommand
- PreprendCommand
- ReplaceCommand
- Maybe OpenDialogCommand which also uses CommandWithAttachedAssetsTrait
Proposed fix: update the class documentation of these commands to indicate they accept render arrays. It could also help to mention best practices about handling render arrays with AJAX commands on https://api.drupal.org/api/drupal/core!core.api.php/group/ajax/
Original issue content:
I am trying to create an Ajax form with three dependent selects, following the Ajax guide provided by Examples module https://api.drupal.org/api/examples/fapi_example!src!Form!AjaxDemo.php/c...
The first two select works perfectly, but the render of third doesn't work, the select in return by a callback function.
Let me show you the fist select code
$form['state'] = array(
'#type' => 'select',
'#title' => $this->t('State'),
'#description' => $this->t('Please select your favorite state'),
'#options' => $this->states,
'#size' => 1,
'#empty_option' => $this->t('-select-'),
'#ajax' => array(
'callback' => '::updateCities',
'wrapper' => 'city-wrapper',
'event' => 'change',
'progress' => array(
'type' => 'throbber',
'message' => "searching",
),
)
);
$form['city_wrapper'] = [
'#type' => 'container',
'#attributes' => ['id' => 'city-wrapper'],
];
Below the callback function
function updateCities($form, FormStateInterface $form_state) {
$state = $form_state->getValue('state');
if(!empty($this->cities[$state])) {
$form['city_wrapper']['city'] = array(
'#attributes' => ['id' => 'city-wrapper'],
'#type' => 'select',
'#title' => $this->t('City'),
'#description' => $this->t('Please select your favorite city'),
'#size' => 1,
'#empty_option' => $this->t('-select-'),
'#options' => $this->cities[$state],
'#ajax' => array(
'callback' => '::updateStudies',
'wrapper' => 'study-wrapper',
'event' => 'change',
'progress' => array(
'type' => 'throbber',
'message' => "searching",
),
)
);
}
$response = new AjaxResponse();
$response->addCommand(new InsertCommand('#taxonomy-term-data-form', $form['city_wrapper']['city']));
return $response;
}
The second select is render properly but without ajax effect to render the third select, I tried returning the FAPI element with the same result.
Here an image of first two select elements working.
In addition, the second select don't allow to be render without selection the first element is immediately selected, I don't know if this a separate issue.