I'm experiencing a weird behavior with an ajax form submit that I cannot find an answer to.

This is my first attempt at making an ajax form so bear with me if I'm making a rookie mistake somewhere.

namespace Drupal\MYMODULE\Forms;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;

class InputForm extends FormBase {

  public function getFormID() {
    return 'authForm';
   * Implements \Drupal\Core\Form\FormInterface::buildForm().
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form['#prefix'] = '<div id="testdiv"></div>';

    $form['key'] = array(
        '#type' => 'textfield',
        '#title' => t('Key'),
        '#size' => 10,
        '#name' => 'key',
        '#required' => TRUE,
        '#attributes' => ['id'=>'key','name'=>'key','class'=>array('input-sm')]

    $form['code'] = array(
        '#type' => 'textfield',
        '#title' => t('code'),
        '#name' => 'code',
        '#required' => TRUE,
        '#attributes' => ['id'=>'code','name'=>'code','class'=>array('input-sm')]

    $form['form_submit'] = array(
        '#type' => 'submit',
        '#value' => t('submit'),
        '#name' => 'submit',
        '#ajax' => [
            'callback' => '::test',
            'prevent' => 'submit',
            'progress' => []
    return $form;
  public function validateForm(array &$form, FormStateInterface $form_state) {

      if (strlen($form_state->getValue('key')) < 10) {
        $form_state->setErrorByName('key', $this->t('Key is too short'));


  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Handle submitted form data.

  public function test(array &$form, FormStateInterface $form_state) {
    if($form_state->hasAnyErrors()) {


    $response = new AjaxResponse();
        new HtmlCommand(
    return $response;



So the erratic behavior is in the validation of the form. What is happening is when I click the submit button (with no values in the input fields) the ajax fires and through var dumping I know the $form_state->hasAnyErrors() returns true but the form rebuild that is returned is not modifying the form highlighting the fields with errors as expected.

So after spending a couple hours trying to sort out this issue I got frustrated and mashed the submit button over and over again in a rage and low and behold it did what it's supposed to do after about eight clicks.

So then I tried to narrow down how many times it would take hitting the button before it would work correctly and it would always happen at random clicks. Sometimes after 2, sometimes after 3... sometimes after 10.

I'm stumped as to why it doesn't happen each and every single time the button is clicked rebuilding the form properly with errors.

Of Note:

  • Have tried removing the prevent key
  • Have tried with Bartik theme with same results
  • Have tried setting an ID on the submit button but then ajax doesn't fire
  • Have confirmed that the form does have errors
  • Have input values into the fields (key value shorter than 10) and the same behavior happens in the validateForm() function

If anyone could point me to what I could be doing wrong I'd very much appreciate it. Thanks in advance!

Edit for visual example: Gif showing behavior

2 Answers 2


What you can see in the Gif looks more like the validation of the form in javascript not in ajax.

Normally you decide in the submit button by including the option 'wrapper' => 'ajax-wrapper-id', if you want to use the wrapper method and return in the ajax callback the new version of the form or parts of it, or use the response method and build an ajax response in the callback. Doubt that you can mix it like this.

If you use the wrapper method you don't need to rebuild the form, because it is already rebuilt, when it reaches the ajax callback.

  • I appreciate the comment but I'm a bit confused. 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. Commented Dec 16, 2016 at 3:24
  • Gah... wrong button pushed. <facepalm> So the form should not pass validation at all given the two fields are required. If I use the wrapper (a div above the form) where the 'content' gets returned does the form then get placed into that div when it fails validation? I assumed the form wouldn't even post to the ajax since the fields were empty but it does and seems like it must make that ajax call before I can send it back with errors which seems awkward. Commented Dec 16, 2016 at 3:30
  • Yes, the required fields are checked in javascript. If you want to get started with ajax, use the wrapper option in the submit button, put the complete form in a div container $form['#prefix'] = '<div id="ajax-wrapper-id">'; and $form['#suffix'] = '</div>'; and return in the ajax callback the complete form return $form;. That's all you have to do and works for 90 % of all use cases.
    – 4uk4
    Commented Dec 16, 2016 at 7:19
  • The required fields are the issue. The client side validation (is the field empty? is so, highlight field and stop all else) is the problem I'm trying to solve. The fields are required and SHOULD highlight and show the tooltip EVERY time the submit button is clicked... but it doesn't. If I remove the #ajax from the submit, it works as expected, but with ajax submit enabled, this functionality becomes erratic. It also seems this is not something new either drupal.stackexchange.com/questions/184700/… Commented Dec 16, 2016 at 16:46
  • Yes, this seems obvious. You have to control, what the click triggers, an ajax request or the client side validation.
    – 4uk4
    Commented Dec 16, 2016 at 19:24

HTML 5 Validation

THIS is the validation that is showing up in the gif. For whatever reason, when doing the ajax call it (HTML5 Validation) would sometimes fire and sometimes not.

My personal solution: Disable HTML5 clientside validation and handle that myself.

$form['#attributes']['novalidate'] = 'novalidate';

Hopefully this helps some future reader.

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.