Home Angular Tutorials Angular Custom Async Validator Example

Angular Custom Async Validator Example

by aouidane.med.amine
85,220 views 15 min read

In this tutorial , we are going to learn how to create a custom Async validator in angular .

We will also see how to use the asyncValidatorFn interface provided by angular to handle async validations and implement our custom async Validator.

Generally , when we use the async keyword , that means , we are talking about asynchronous communication which will be explained and used in the custom async validator example.

Table of Contents

How To Create Custom Async Validator in Angular ?

Generally , when we create forms, we must control the user inputs to secure the application and also have better user experience by showing the right message to the user.

For example, if the user wants to enter the age, he must not be allowed to put a number  < 1 and > 100 , that’s why we use the angular validator to control the user input and show the right error message to the user.

In the previous case , we have used the sync validator because we can check the input validation in real time.

 In some cases ,  we can’t check the validation because we don’t have the data locally,

For example , if we want to check the email existence when the user enters his email, we need to access the database and check if the email already exists or not.

To do that we just need to create a custom Async validator and check the email existence using asynchronous API calls.

How To Use Asynchronous Service in Angular ?

In our example , we are going to validate the email input by checking if it exists in the database or not.

To do that we will create a custom async Validator that uses an asynchronous service to get the data from the database which returns the result as observable or promise.

Generally we need to make an API call that returns an observable then we subscribe it in the custom Async validator.

As we don’t have an Api , we will mock the response and make a fake observable that returns the same result as the Api call.

				
					import {Observable, of} from "rxjs";

export class UserService {

   emailListMock = [
     'email_1@gmail.com',
     'email_2@gmail.com',
     'email_3@gmail.com',
     'email_4@gmail.com',
     'email_5@gmail.com',
     'email_6@gmail.com',
   ]

  isEmailExist(email: string): Observable<boolean> {
     // we can make API call here instead.
     const isEmailExist = this.emailListMock.indexOf(email) != -1;
     return of(isEmailExist);
  }
}

				
			

To use the service in our application , we need to add it to the providers of the application module ( check the line 29 bellow)

				
					import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {ReactiveFormsModule} from "@angular/forms";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInputModule} from "@angular/material/input";
import {MatCardModule} from "@angular/material/card";
import {MatButtonModule} from "@angular/material/button";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {UserService} from "./service/user-service";


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule,
    MatFormFieldModule,
    MatInputModule,
    MatCardModule,
    MatButtonModule,
    BrowserAnimationsModule
  ],
  providers: [UserService],
  bootstrap: [AppComponent]
})
export class AppModule { }

				
			

How To Use AsyncValidatorFn Interface ?

To handle async validations , Angular provides AsyncValidatorFn Interface that helps us to create custom async validators.

The asyncValidatorFn interface contains a function declaration that has AbstractControl as an argument.

The AbstractControl argument contains the latest formControl value.

the function that will be implemented must return  Observable<ValidationErrors | null> or Promise<ValidationErrors | null>

In the previous part of the tutorial , we have seen how to implement the service that returns an observable.

Now , we just need to inject the service into the custom Async validator and use it inside the function.

				
					import {UserService} from "./user-service";
import {AbstractControl, AsyncValidatorFn, ValidationErrors} from "@angular/forms";
import {debounceTime, Observable, of} from "rxjs";

export function isEmailExistValidator(userService: UserService): AsyncValidatorFn {
  return (formControl: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    let emailValidation = null;
     userService.isEmailExist(formControl.value)
       .pipe(debounceTime(2000))
      .subscribe(isEmailExist => {
        if (isEmailExist) {
          emailValidation =  { "isEmailExist": true };
        }
      })

    return of(emailValidation);
  };
}
				
			

Async Validator using Reactive-forms Approach

Till now , we have the service that returns the observable and the custom async validator.

Now , we will build the form structure using the reactive forms approach , that means , we will create the form and handle the validation in the typescript file.

You can also use the template-driven forms approach

So , we will use the formBuilder and formGroup to make the form structure and set the async validator inside.

Then we will add the formControl elements inside the HTML template and add the error message for each form element.

				
					//app.component.ts

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
import {isEmailExistValidator} from "./service/email-validator";
import {UserService} from "./service/user-service";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  title = 'Angular Custom Async Validator Example';

  userForm: FormGroup = new FormGroup({});

  constructor(private formBuilder: FormBuilder, private userService: UserService) {
  }

  ngOnInit() {
    this.initializeForm();
  }

  initializeForm(): void {
    this.userForm = this.formBuilder.group({
      pseudo: new FormControl('XperTuto', Validators.required),
      name: new FormControl('Aouidane', Validators.required),
      email: new FormControl('',
        [Validators.required],
        [isEmailExistValidator(this.userService)]),
    });
  }

  adduser() {
    // call API
    console.log(this.userForm.getRawValue());
  }
}

				
			
				
					<!--app.component.html-->

<form [formGroup]="userForm" (ngSubmit)="adduser()">

  <mat-card>
    <mat-form-field appearance="fill">
      <mat-label>Pseudo*</mat-label>
      <input matInput type="text" formControlName="pseudo" placeholder="Pseudo">
      <mat-error *ngIf="this.userForm.controls['pseudo'].hasError('required')" >
        <p>The Pseudo is required</p>
      </mat-error>
    </mat-form-field>

    <mat-form-field appearance="fill">
      <mat-label>Name*</mat-label>
      <input matInput type="text" formControlName="name" placeholder="Name">
      <mat-error *ngIf="this.userForm.controls['name'].hasError('required')" >
        <p>The Name is required</p>
      </mat-error>
    </mat-form-field>

    <mat-form-field appearance="fill">
      <mat-label>Email*</mat-label>
      <input matInput type="text" formControlName="email" placeholder="Email">
      <mat-error *ngIf="this.userForm.controls['email'].hasError('isEmailExist')" >
        <p>The Email is already exist</p>
      </mat-error>
      <mat-error *ngIf="this.userForm.controls['email'].hasError('required')" >
        <p>The Email is required</p>
      </mat-error>

    </mat-form-field>
  </mat-card>

  <mat-card-actions>
    <br>
    <button type="submit" mat-raised-button color="primary">Submit</button>
  </mat-card-actions>

</form>

				
			

We will get someting like that as a result : 

Angular Custom Async Validator

References

Clone The Project

To download and run the example, please follow the steps :

1- Clone the source code from XperTuto git repository using the following command : 

git clone https://github.com/www-xperTuto-com/Angular-Custom-Async-Validator-Example.git

2- Install the node dependencies using NPM command line  : npm install

3- Run the application using the following command : ng serve 

4- Access to the project through the URL : http://localhost:4200 

Read More About Angular Forms :

Recapitulation

In this tutorial, we have seen how to create a custom Async validator using the reactive-forms approach and we have explained how to use AsyncValidatorFn  that is provided by angular.

We have also seen the difference between the sync validator and the async validator and when we should use them exactly through a simple example.

I hope you enjoy reading this Tutorial.

You may also like