Angular Custom Validator In Reactive Forms

Introduction

We have seen how we can use reactive forms by creating form control objects. In this post, we will see how to implement the ngx-mat-select-search to enable search in dropdowns in reactive forms. In the earlier post, Implementing Search In DropDown Field In Angular Using NgxMatSelectSearch we have seen how to use the ngx-mat-select-search with template-driven forms. In this post, we will see how to implement a custom validator in Reactive Forms. 

Custom Validator

We have to create custom validation in reactive forms. For this, we will use ValidatorFn which is an interface defined in @angular/forms. The signature of this function will receive a parameter of type AbstractControl and returns ValidationErrors or null. We can use interfaces to define the shape of our functions and objects. So any function that defines the below signature is defined as a Validator function. 

interface ValidatorFn {
  (control: AbstractControl): ValidationErrors | null
}

We can define a Validator function anywhere in our application. We will pull the validator function inside a class. Using a class to wrap up this function, we follow the feature of encapsulation by keeping the validator functions in a single class. 

Implementing A Custom Validator

We will now create a new file called name.validator.ts in some component folder. Now we will export a class called nameValidator. Now we want to set validation for all the name fields in our application that it should contain space. We will define the signature of this function as ValidatorFn. This function should take a parameter of type AbstractControl(which is part of @angular/forms). 
Now, this method should return either ValidationErrors(also part @anguar/forms) or null. Now we will set a validation rule by taking the control.value as a string and checking if the space is present or not. If space is present then we will return ValidationErrors. This ValidationErrors defines an object that has one or more key-value pairs. Keys are strings and values can be anything. So we should return the key which is the name of the validation error. In our case it is noSpaceName. As a value, we can return either boolean true or we a complex object if we want to show some details to the user. So here we will return noSpaceName(of type ValidatorErrors) with value as true if space is present in the name otherwise returns null. Now we will define the noSpaceName function as a static method. So that we can directly call this function using the class name(nameValidator.noSpaceName) without creating an instance of this class.
Now let us see the code for name.validator.ts

// name.validator.ts file
import {AbstractControl, ValidationErrors } from '@angular/forms';

export class NameValidators{
   static noSpaceName(control: AbstractControl): ValidationErrors | null{
     if((control.value as string).indexOf(" ")>= 0){
        return {noSpaceName: true}
      }
    return null;
   }
}

Reactive Forms Validation Component

We already have a reactive-form component which we have seen in the 
How To Validate Angular Reactive Forms. We will be adding the custom validator in the same component for the username field. 

//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)
     ]), 
     password: new FormControl('', Validators.required)
    });

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

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

Adding CustomValidator To Component Class

We will add our custom validator in the array of validators shown as below:

//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');
    }
}

Adding Validation To Template

Now we will go to our component template and write the below error code for the custom validator. Now the error will be NoSpaceName which is name of ValidationFn.

// 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>

Comments