The setup:

I have a Drupal 8 form created manually, and I would like to recreate it using Webform module.

So far, I have managed to recreate everything (required fields and submission handler), but there is a last bit missing: the client-side validation that triggers on change.

The initial form have fields defined as follows:

$form['first-name'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('First name'),
  '#required' => true,
  '#required_error' => $this->t('This is a required field. Please enter your first name.'),
  '#ajax' => [
    'callback' => '::validateFnameAjax',
    'effect' => 'fade',
    'event' => 'change',
    'progress' => FALSE,

Calling validation functions like this one:

public function validateFnameAjax(array &$form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $errorValidation = '<div class="form-item-message form-item__error-message"><small class="text-danger"></small></div>';
  $successValidation = '<div class="form-item-message form-item__success-message"><small class="text-success"></small></div>';
  // Assert the name field has been included
  if (!$form_state->getValue('first-name') || empty($form_state->getValue('first-name'))) {
    $message = $this->t('This is a required field. Please enter your first name.');
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'removeClass', ['is-valid']));
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'addClass', ['is-invalid']));
    $response->addCommand(new ReplaceCommand('.js-form-item-first-name .form-item-message', $errorValidation));
    $response->addCommand(new HtmlCommand('.js-form-item-first-name .text-danger', $message));
  else {
    $message = $this->t('Your first name is valid.');
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'removeClass', ['is-invalid']));
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'addClass', ['is-valid']));
    $response->addCommand(new ReplaceCommand('.js-form-item-first-name .form-item-message', $successValidation));
    $response->addCommand(new HtmlCommand('.js-form-item-first-name .text-success', $message));
  return $response;

In the new webform function I have changed lines one and six, but the rest of the code is the same.

New line one:

  public function validateFnameAjax(WebformSubmissionInterface $webform_submission) {

New line 6:

  if (!$webform_submission->getElementData('first_name') || empty($webform_submission->getElementData('first_name'))) {

The challenge:

Implement the same behaviour: trigger the custom validation when the content in the field changes (before submission).

Current status:

I have tried to include the Ajax properties using the Advanced tab of the edit field UI as you can see in the following screenshots:

enter image description here

enter image description here

But I get this error in the log:

Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder->buildResponse() (line 67 of /var/www/drupalvm/web/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php).

Does anyone know what am I doing wrong to get this error, and how to make this client side validation work?

2 Answers 2


Although this answer does not provide the solution to add custom client-side validation to a Webform field using advanced custom properties, it provides a working alternative to add client-side validation to a Webform field.

Given that Webform UI comes with the option to add custom CSS and Javascript to a Webform, I have made use of this option to add my custom validation.

enter image description here


I think @ygoex has a very good working alternative. Just a few things that catches my attention when reading the problem. I noticed the red star on your screenshot of the elements(YAML). Did you save your changes? If yes did you also reinstall or rebuild the cache (if it is Yaml you useably have to reconstruct the configuration). I know, maybe some dumb remarks, but if you are a bit like me.....

Secondly (forgive me if I make no sense, I'm not a Drupal guru), the poundsign # is used in render arrays to recognize key-properties (https://www.drupal.org/docs/drupal-apis/render-api/render-arrays). So maybe excluding the # give the right result?

Edit: added "secondly" and changed some typo's

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.