DrupalのAjax処理内で使用できるコマンドと、それを使ったフォームのバリデーションを紹介したいとおもいます

まずサンプルのフォームを作成します。

modules/custom
└── ajax_form
    ├── ajax_form.info.yml
    ├── ajax_form.routing.yml
    └── src
        └── Form
            └── AjaxForm.php

ajax_form.info.yml

name: 'Ajax Form'
type: 'module'
description: 'Ajax Form'
core: '8.x'

ajax_form.routing.yml

ajax_form.form:
  path: '/ajax-form'
  defaults:
    _form: '\Drupal\ajax_form\Form\AjaxForm'
    _title: 'Ajax Form'
  requirements:
    _permission: 'access content'

AjaxForm.php

<?php

namespace Drupal\ajax_form\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Ajax Form.
 */
class AjaxForm extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'ajax_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['sample'] = [
      '#type' => 'textfield',
      '#title' => 'sample',
    ];

    $form['ajax'] = [
      '#type' => 'submit',
      '#value' => 'ajax',
      '#ajax' => [
        'callback' => [$this, 'validateAjax'],
        'event' => 'click',
        'progress' => [],
      ],
      '#suffix' => '<span id="ajax-validate-message"></span>'

    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateAjax(array &$form, FormStateInterface $form_state) {
    $sample = $form_state->getValue('sample');
    if (empty($sample)) {
      $form_state->setErrorByName('sample', 'invalid value');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    return $form;
  }

}

本来フォームのバリデーションは validateForm メソッドで行われますが、
Ajaxを使用する場合、callback に指定されたメソッド(validateAjax)で行います。そして

 /**
   * {@inheritdoc}
   */
  public function validateAjax(array &$form, FormStateInterface $form_state) {
    $sample = $form_state->getValue('sample');
    if (empty($sample)) {
      $form_state->setErrorByName('sample', 'value is empty.');
    }
  }

と実装したいところですが、Ajaxを使用すると、この setErrorByName は動作しません。代わりにこのように実装します

namespace Drupal\ajax_form\Form;

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

/**
 * Ajax Form.
 */
class AjaxForm extends FormBase {
  /**
   * {@inheritdoc}
   */
  public function validateAjax(array &$form, FormStateInterface $form_state) {
    $sample = $form_state->getValue('sample');
    if (empty($sample)) {
      $message = 'value is empty.';
    }
    else {
      $message = 'success.';
    }

    $response = new AjaxResponse();
    $response->addCommand(
      new HtmlCommand('#ajax-validate-message', $message)
    );
    return $response;
  }

この実装で、sampleに何も入力せずにajaxをクリックすると、下の画像のようになり、意図したバリデーションが効いていることが確認できます。

追加したコードを1つずつ見ていきましょう。 まずAjax レスポンス用インスタンスを生成します

$response = new AjaxResponse();

HtmlCommand はjQueryのhtml() メソッドを使用して、指定した要素内のテキストを、指定した文字列に変更することができます。

$response->addCommand(
  new HtmlCommand('#ajax-validate-message', $message)
);

このコマンドの種類は他にもたくさんあるので、いくつか紹介したいと思います。

InvokeCommand

jQueryの任意のメソッドを使用することができます。
例)sampleフォームにerror クラスを追加します

$response->addCommand(
  new InvokeCommand('#edit-sample', 'addClass', ['error'])
);

CssCommand

jQueryのcssメソッドを使用することができます
例)バリデートメッセージにcolor: red;を適用します

$response->addCommand(
  new CssCommand('#ajax-validate-message', ['color' => 'red'])
);

ReplaceCommand

jQueryのreplaceメソッドを呼び出し、第1引数で指定された要素を、第2引数で指定した要素に入れ替えます。
例)#ajax-validate-message要素を<div>replaced</div>に入れ替えます

$element = array(
  '#type' => 'markup',
  '#markup' => 'replaced',
  '#prefix' => '<div>',
  '#suffix' => '</div>',
);
$response->addCommand(
  new ReplaceCommand('#ajax-validate-message', $element)
);

他にもモーダルを表示するコマンドなどあるので、下記のリンクを参考に実装してみてください! https://api.drupal.org/api/drupal/core%21core.api.php/group/ajax/8.6.x