Implementing Asynchronous/Server Validations In Angular Reactive Forms

Async Validation

Introduction

We have learned about making FormControls with reactive forms in Angular. In some scenarios, we need to add asynchronous validations to make things work as per our requirement. 

When we need asynchronous validations?
Asynchronous means non-blocking. It means the process doesn't block while waiting for a response from a server or timer functions such as setTimeout(). All these are called asynchronous operations. Sometimes we have to check our form controls values to be validated from the server such as a unique username or email. We can use custom async validators in these cases. Now we will see how to implement async validators using reactive forms in Angular.

Implementing Async Validators In Angular Reactive Forms

Now we implement async validator we will go to our form Control object and check the constructor of FormControl. The first parameter is the initial value, the second one is one or an array of validator functions. Now if we look at the third parameter is the async validator of type async validator function or an array of async validator functions. 

Async validators implement the AsyncValidatorFn or AsyncValidator interfaces. To perform async validation using reactive forms we will use the AsyncValidator interface. This validator function takes a parameter of type AbstractControl and returns the promise or observable of ValidationErrors or null. 

interface AsyncValidatorFn {
  (control: AbstractControl): Promise<validationerrors null=""> | Observable<validationerrors null="">
  }

Form And Async Validator

We have an Angular project and a reactive-form component which has a form that has two fields: username and password. We will go to the reactive-form.component.html file and write the below code. Here I have designed a form using Bootstrap. We have also implemented validations and custom validations for the username field in our form. Now we will see how to add asynchronous validation for the username field. We want our username to be unique and to check the uniqueness we will implement a call to the server API. 

Example
// reactive-form.component.html file
<form [formGroup]="form">
  <div class="form-group">
    <label>Email</label>
    <input type="email" class="form-control" id="userName" placeholder="Enter email" formControlName="username">
    <div *ngIf="username.touched && username.invalid" class="alert alert-danger">
      <div *ngIf="username.errors.required"> Username is required </div>
      <div *ngIf="username.errors.minlength">
        Username should be minimum {{ username.errors.minlength.requiredLength }} characters</div>
      <div *ngIf="username.errors.noSpaceName"> Username cannot contain space
      </div>
    </div>
  </div>
  <div class="form-group">
    <label>Password</label>
    <input type="password" class="form-control" id="password" placeholder="Enter password" formControlName="password">
    <div *ngIf="password.touched && password.invalid" class="alert alert-danger">
      <div *ngIf="password.errors.required"> Password is required </div>
    </div>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>
Now we will the component.ts file where we have defined FormGroup and FormControl objects.

//reactive-forms.component.ts file
import { Component, OnInit} from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';  // Importing the Validators class
@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
  styleUrls: ['./reactive-form.component.scss']
})
export class ReactiveFormComponent { 

   form = new FormGroup({
     username: new FormControl('', [ 
      Validators.required,    // passing reference to required method of Validators class
      Validators.minLength(3),
      NameValidators.noSpaceName // adding custom validator here
     ]), 
     password: new FormControl('', Validators.required)
    });

   get username(){
    return form.get('username');
    }

  get password(){
    return form.get('password');
    }
}
This is our name.validator.ts file where we have already implemented a custom validator for the username field. Now in the same file, we will add AsyncValidator for checking the uniqueness of the name field. We have defined a static method uniqueName which is of type AsyncValidatorFn. We are returning observable here of type ValidationErrors or null. We will inject the userservice in the constructor object of our NameValidators class. We are calling our UserService to check if the username is unique or not. We are using the checkDuplicateName method to call the server.

// name.validator.ts file
import {AbstractControl, ValidationErrors } from '@angular/forms';
 constructor(UserService: userservice)
export class NameValidators{
   static noSpaceName(control: AbstractControl): ValidationErrors | null{
     if((control.value as string).indexOf(" ")&gt;= 0){
        return {noSpaceName: true}
      }
    return null;
   }

// ADDING ASYNC VALIDATION FOR USERNAME
   static uniqueName(control: AbstractControl): Observable<ValidationErrors | null>{
        return this.UserService.checkDuplicateName(control.value)
        .pipe(map(res => {
            // if username is already taken
            if (res) {
              return { 'uniqueName': true};
            }
           else 
             return null;
          })
        );
    }
  }

Adding AsyncValidator To Component Class

Now we will add our async validator uniqueName to our name FormControl in our component.ts file as follows. We will add it as the third parameter of the FormControl object. 

//reactive-forms.component.ts file
import { Component, OnInit} from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';  // Importing the Validators class
@Component({
  selector: 'app-reactive-form',
  templateUrl: './reactive-form.component.html',
  styleUrls: ['./reactive-form.component.scss']
})
export class ReactiveFormComponent { 

   form = new FormGroup({
     username: new FormControl('', [ 
      Validators.required,    // passing reference to required method of Validators class
      Validators.minLength(3),
      NameValidators.noSpaceName // adding custom validator here
     ], NameValidators.uniqueName),  // Adding async validator as third parameter
     password: new FormControl('', Validators.required)
    });

   get username(){
    return form.get('username');
    }

  get password(){
    return form.get('password');
    }
} 

Adding Async Validation To Template

Now we will add username already exists error in our template as follows.

// reactive-form.component.html file
<form [formGroup]="form">
  <div class="form-group">
    <label>Email</label>
    <input type="email" class="form-control" id="userName" placeholder="Enter email" formControlName="username">
    <div *ngIf="username.touched && username.invalid" class="alert alert-danger">
      <div *ngIf="username.errors.required"> Username is required </div>
      <div *ngIf="username.errors.minlength">
        Username should be minimum {{ username.errors.minlength.requiredLength }} characters</div>
      <div *ngIf="username.errors.noSpaceName"> Username cannot contain space
      </div>
      <div *ngIf="username.errors.uniqueName"> Username already exists
      </div>
    </div>
  </div>
  <div class="form-group">
    <label>Password</label>
    <input type="password" class="form-control" id="password" placeholder="Enter password" formControlName="password">
    <div *ngIf="password.touched && password.invalid" class="alert alert-danger">
      <div *ngIf="password.errors.required"> Password is required </div>
    </div>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

Comments