How To Validate Angular Reactive Forms

Introduction

In this post, we will see how to add validations to reactive forms. In the last post, we have seen how to add FormControls for a form in reactive forms. Generally, when we use template-driven forms, we will add HTML5 attributes such as required, minlength, maxlength, etc. But in reactive forms, we will not use these attributes. Rather we will assign validators here while creating form control objects in our component class. Let us see how it works.

Assign Validators 

In the last post, we have seen how to create form control objects in reactive forms. We already have a sign-up form in which we have created two control objects: one for username and another for the password. We already have the below code in our component.ts file:

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

   form = new FormGroup({
     username: new FormControl(),
     password: new FormControl()
    });
}

Now we will assign validators for the control objects while creating it. When we see the constructor of the formControl class, we can see that we have different parameters. 
  • The first parameter is the form state which indicates the initial value for this form-control. It is an optional parameter. For now, I will set it to an empty string.
  • The second parameter is the validator and it is also an optional parameter. If the see the type of this validator, it is either a validation function denotes by F or it can be an array of validator functions.

What are the validator functions?

In Angular, we have a class called validators which is part of @angular/forms module. So to use this class, we will auto-import the Validators class at the top of the component class. So all the validators such as required, minlength exists here in this class as well. These validators are methods defined in this Validators class. We are accessing these methods directly using the validators class instead of creating a new instance of validators class and then calling the methods. We are able to do so as these methods are static members. When a method is defined as static using the static keyword it can be accessed directly using the class name.

Now let us add validators to the form control objects. We can pass the reference to the Validators.email method as the constructor of the form control requires a validator function which is a function reference. If we want to add multiple validators to the form-control object, we can add an array of validator functions. Now in this example, I am adding two validators for username and one validator for the password. 

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

Showing Error Specific Messages In Reactive Forms

To get access to the form control objects, we used to write username="ngModel" and then use this username to show different forms in a template-driven approach. But in reactive forms, we can't proceed using ngModel. We have to get access to the underlying form-control objects which we created in our component class. Now we have a form field that we can access in our template. We will use form.get('username') to get access to the username form-control object using get method. Using this get() method we can get access to any form control object present in this form group. After getting access to these objects we can use various controls present in the form control class.
  • Valid - This is true if all the controls in the form are valid. It's the state of the validity of all form-controls.
  • Invalid - It is the opposite of valid. True if any form input control is invalid.
  • Pristine - True if no field is modified yet. It conveys about the cleanliness of the form.
  • Dirty - Opposite of pristine. True if some control is modified.
  • Touched - If any field is touched yet or not. True if touched. 
  • Untouched - Opposite of untouched. 
Now we will write specific error messages in the template.

<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="form.get('username').touched && form.get('username').invalid" class="alert alert-danger">
         <div *ngIf="form.get('username').errors.required"> Usernameis required </div>
        <div *ngIf="form.get('username').errors.minlength" class="alert alert-danger"> 
        Username should be minimum {{ username.errors.minlength.requiredLength }}  characters</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="form.get('password').touched && form.get('password').invalid" class="alert alert-danger">
    <div *ngIf="form.get('password').errors.required"> Password is required </div>
   </div>
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

The above code is noisy as we have to write form.get('username') again and again in the template. Now we will go to component.ts file and write the below code. Here we will define a property that will give access to these form control objects. The username and password(form control objects) are actually methods under the hood. So we will define a getter method and call this username(form control object) as a method and in the definition of this method, (also called property) we will return form.get('username'). Now we will go to our component.ts file and write the below additional code.

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

Now we can access these properties in our template directly shown as below:

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