Asynchronous Form Validation in Angular

I'm writing an app using Ionic 3. I need to validate a form input in a template-driven form. To do that, I need to send a HTTP request and have the server to validate it and return the result. The validation is asynchronous. How do I validate the input asynchronously? Angular provides NG_VALIDATORS for synchronous form validation, as well asĀ  NG_ASYNC_VALIDATORS for asynchronous form validation. OK. Let's do it.

First, Let's start a new app.

ng new async-validation

To demonstrate the idea, I'll just add a form with an input in app.component.ts. It can be much more complex but the idea is still the same.

<form>
    <div>
        <label for="name">Name</label>
        <input type="text" name="name" myExistingName [(ngModel)]="name" #nameField="ngModel">
    </div>
</form>

This is a simple form with only one input. Straightforward, right? But what is myExistingName? That is the async validator I'm going to demonstrate. It's a directive in Angular.

Here is the code:

import { Directive } from "@angular/core";
import { NG_ASYNC_VALIDATORS, AbstractControl, Validator } from "@angular/forms";

interface ValidationResult {
}

@Directive({
  selector: '[myExistingName]',
  providers: [{provide: NG_ASYNC_VALIDATORS, useExisting:MyExistingNameDirective, multi: true}]
})
export class MyExistingNameDirective implements Validator {
  private static secretPattern: RegExp = new RegExp('^secret[a-zA-Z]*');

  validate(control: AbstractControl): Promise<ValidationResult> {
    const value: any = control.value;
    const hasValue: boolean = (value !== undefined) && (value !== null);
    let isPattern: boolean = false;

    if (hasValue) {
      isPattern = MyExistingNameDirective.secretPattern.test(value);
    }

    if (!hasValue || !isPattern) {
      return Promise.resolve({
        'invalidNamePattern': true,
      });
    } else {
      return Promise.resolve(null);
    }
  }
}

This class implements Validator interface. You may notice that validate returns a Promise. With that, you can return the result asynchronously. You can send http request and resolve that to get the result. Also pay attention to the directive decorator. It provides a NG_ASYNC_VALIDATORS and it's using useExisting and allows multi. Those are what you need for the async validation. In my demonstration, I check the input value. If the value doesn't begin with "secret", it'll mark an error code invalidNamePattern.

What do we do with the error code invalidNamePattern? I define it and it's used in the form to display the error.

Here is the updated form.

<form>
    <div>
        <label for="name">Name</label>
        <input type="text" name="name" myExistingName [(ngModel)]="name" #nameField="ngModel">
    </div>
    <div *ngIf="nameField.errors && !nameField.pristine">
        <div *ngIf="nameField.errors.invalidNamePattern">
            It's not a secret name.
        </div>
    </div>
</form>

 

Note that I need to check whether errors is set. Since it's asynchronously, sometimes it's not set yet but invalid is set. If you depends on invalid to decide whether to show error message and then use errors in showing error message, then you may find that errors is not defined while invalid is true.

With that, when I type in text that doesn't begin with "secret", it shows me an error.

That looks simple, right? When you write your own synchronous form validator, you'll find that there are only three differences for asynchronous counterpart:

  • Provide NG_ASYNC_VALIDATORS
  • Return Promise
  • Use errors to decide whether to show the error message.

Hope you find it useful. Please let me know if you have any helpful tips.

Facebooktwitterredditlinkedintumblr

Leave a comment

Your email address will not be published. Required fields are marked *