In this article, I will explain how to implement Async Validation In Angular. Angular does not provide built-in type async Validation implmentation, it provides only for sync validation. The implementation of async validator is very similar to the sync validator. The only difference is that the async Validators must return the result of the validation as an observable or as Promise. In this article, we will create an async validator for the Existing user. We will check the user exists or not using async validator.

Prerequisites
    Angular 12
    HTML/Bootstrap

For this article, I have created an Angular project using Angular 12. For creating an Angular project, we need to follow the following steps:

Create Project
I have created a project using the following command in the Command Prompt.
ng new AsyncValidatorExample

Open a project in Visual Studio Code using the following commands.
cd AsyncValidatorExample
Code .


Now in Visual Studio, your project looks like as below.


Rules for Async Validator
For creating an Async Validator there are following rules which need to be followed:
    The function must implement the AsyncValidatorFn Interface, which defines the signature of the validator function.
    The function should be returned in following observable or promise.
    If input is valid then must return null, or ValidationErrors if the input is invalid.

AsyncValidatorFn
AsyncValidatorFn is a predefine interface which defines the type of the validator function.

Signature of AsyncValidatorFn
interface AsyncValidatorFn {
  (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>
}

Let's create User Service that will check input user exists or not. For now, we will check it with local parameter not will API call.
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  private usernames = ['Gajendra', 'Rohit', 'Rohan', 'Ajay'];

  checkIfUsernameExists(value: string) {
    return of(this.usernames.some((a) => a.toLocaleUpperCase() === value.toLocaleUpperCase())).pipe(
      delay(1000)
    );
  }
}

Let's create Async Validator to check if the username exists against that method.
import {
  AbstractControl,
  AsyncValidatorFn,
  ValidationErrors,
} from '@angular/forms';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { UserService } from './user.service';

export class UsernameValidator {
  static createValidator(userService: UserService): AsyncValidatorFn {
    return (control: AbstractControl): Observable<ValidationErrors> => {
      return userService
        .checkIfUsernameExists(control.value)
        .pipe(
          map((result: boolean) =>
            result ? { usernameAlreadyExists: true } : null
          )
        );
    };
  }
}


Use Of Async Validator

this.fb.group({
    username: [
      null,
      [UsernameValidator.createValidator(this.userService)],
      updateOn: 'blur'
    ],
  });


App.Component.ts
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { UserService } from './user.service';
import { UsernameValidator } from './username-validator';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent {
  constructor(private fb: FormBuilder, private userService: UserService) {}

  registrationForm = this.fb.group({
    username: [
      null,{
      asyncValidators:[UsernameValidator.createValidator(this.userService)],
      updateOn: 'blur'
}
    ],
  });
}


App.Component.html
<mat-form-field>
  <mat-label>Username</mat-label>
  <input matInput placeholder="Enter User Name here" formControlName="username" />
  <mat-error
    *ngIf="form.get('username').hasError('usernameAlreadyExists')"
  >
    Username already <strong>exists</strong>
  </mat-error>
</mat-form-field>

Let's Run the Project