I have a Drupal 8 form that includes the following fields:

// Comtainer for message fields and preview.
$form['email'] = array(
  '#prefix' => '<div id="email">',
  '#suffix' => '</div>',

// Template selection drop-down.
$form['template']['template_select'] = array(
  '#type' => 'select',
  '#title' => $this->t('Select template to use (overwrites message)'),
  '#options' => $options,
  '#default_value' => 1,
  '#ajax' => array(
    'wrapper' => 'email',
    'callback' => array($this, 'updateEmailTemplate'),
    'event' => 'change',

$form['email']['message']['subject'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('Message subject'),
  '#default_value' => $params['subject'],
  '#ajax' => array(
    'wrapper' => 'preview',
    'callback' => array($this, 'updateEmailPreview'),
    'event' => 'change',

$form['email']['message']['body'] = array(
  //'#type' => 'textarea',
  '#type' => 'text_format',
  '#title' => $this->t('Message body'),
  '#description' => $this->t('Text for the email body. you may use the following tokens: @tokens.', ['@tokens' => SimpleConregTokens::tokenHelp()]),
  '#default_value' => $params['body'],
  //'#value' => $params['body'],
  '#format' => $params['format'],

My Ajax callback function is:

  // Callback function for Template drop down - load message fields with template.
  public function updateEmailTemplate(array $form, FormStateInterface $form_state) {
    $form_values = $form_state->getValues();
    $templates = $form_state->get('templates');

    if (!empty($template = $form_values['template']['template_select'])) {
      //Code here to determine new form values.

      $form['email']['message']['subject']['#value'] = $params['subject'];
      //$form['email']['message']['body']['#type'] = "textarea";
      $form['email']['message']['body']['#value'] = $form['email']['message']['body']['#default_value'];
      $form['email']['message']['body']['#format'] = $params['format'];
    return $form['email'];

So what's happening?

When the user chosen selects a template from the dropdown, the text_format doesn't update.

If I change the #type of the field from text_format to textarea, it will update without any problems, which suggests the callback should work.

I though there might be something in the text editor JavaScript preserving the text_format field from being updated, but if I explicitly set the #value in the form building function, it starts updating in the callbacks. The problem with this is that it also overrides the value when the form is submitted.

Finally, I've tried adding a line to the callback to set the #type to a textarea, and this is also getting ignored. The callback is still getting called, however, because the supbect textbox does update.

Any ideas where I'm going wrong? I'm particularly confused as to why the textarea version works perfectly but changing to text_format doesn't.


  • Suggestion: print multiple versions of this field (one for each format) and use drupal states to switch between them. The user experience would be much smoother.
    – Eyal
    Mar 18, 2017 at 10:51
  • That's actually a really good idea, and should mean that if the user edits the text_format field for one template, then selects a different one, but later goes back to the first one, their edits should be retained. Thanks. Mar 18, 2017 at 11:38
  • This worked perfectly, and allowed me to cut out almost all of the code in the callback, so simplified my form considerably. Mar 18, 2017 at 12:28

1 Answer 1


Don't change $form in an ajax callback. See this documentation for D7:


Details and Warnings

Changes to the form must only be made in the form builder function, or validation will fail. The callback function must not alter the form or any other state.

For D8 there is in the moment only documentation for the new syntax, but not for the basics. Use the D7 doc's instead.

  • I see what you're saying, the bit about form validation is certainly important. The problem is how can I detect when the dropdown value has changed in the form builder? Mar 18, 2017 at 11:33
  • You check the dropdown value in $form_state. The same code you have now in the callback should work in the form builder.
    – 4uk4
    Mar 18, 2017 at 12:16
  • Oh I think I see. That should work, but @Eyal's suggestion has the advantage that I only ever need to set the #default_value, never the #value, which makes things simpler. Mar 18, 2017 at 12:33
  • Yes, this is a good suggestion from @Eyal's. Btw. my answer is about the ajax callback, I didn't mean that you should use exactly this code, it's about moving the code from the callback to the form builder.
    – 4uk4
    Mar 18, 2017 at 13:04
  • Appreciate that, and everything in your answer is correct. Mar 18, 2017 at 13:21

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.