Menu Close

Angular Reactive Forms Validation


In this lesson we will learn about how Angular Reactive Forms Validation works. One of the common tasks that is performed, while building a form is Validation. Here we will learn how to build Reactive Forms and apply Built-in validators. Please read my previous article Reactive Forms in Angular.

#Find Source Code

How to add a Validator to Reactive Forms

We configure the validators as the second and third argument to the FormControlFormGroup or FormArray in the component class. The second argument is a collection of sync validators and the third argument is a collection of an async validators.

What is Validator?

A Validator is a function that checks the instance of FormControlFormGroup or a FormArray and returns a list of errors. If the Validator returns a null means that validation has passed

Built-in Validators

The Angular ReactiveForms Module provides several Built-in validators out of the box. They are required, minlength, maxlength & pattern etc.

Reactive Forms Validation Example

Here we created Angular-14 project and we will now add some of the built-in validators. At the end of article you can see the reactive forms validation like below.

Reactive-forms-validation

Form Model

The below is the contactForm model that we created to check validation. Here we validate sample form with custom validation.

contactForm = new FormGroup({
    firstname: new FormControl('',[Validators.required,Validators.minLength(10)]),
    lastname: new FormControl('',[Validators.required, Validators.maxLength(15), Validators.pattern("^[a-zA-Z]+$")]),
    email:new FormControl('',[Validators.email,Validators.required]),
    gender: new FormControl('',[Validators.required]),
    isMarried: new FormControl('',[Validators.required]),
    country: new FormControl('',[Validators.required]),
    address:new FormGroup({
      city: new FormControl('',[Validators.required]),
      street: new FormControl('',[Validators.required]),
      pincode:new FormControl('',[Validators.required])
    })
  })

Adding in Built-in Validators

The mentioned earlier, the Angular has provided several built-in validators out of the box.

Required Validator

The required validator is a sync validator, which returns true only if the formcontrol has a non-empty value entered. The second argument of the FormControl takes the Sync Validator.

firstname: new FormControl('',[Validators.required]),

Minlength Validator

Minlength validator requires the control value must not have less number of characters than the value specified in the validator. For Example, minlength validator ensures that the firstname value has at least 10 characters.

firstname: new FormControl('',[Validators.required,Validators.minLength(10)]),

Pattern Validator

This Validator requires that the control value must match the regex pattern provided in the attribute. For example, the pattern ^[a-zA-Z]+$ ensures that the only letters are allowed (even spaces are not allowed). Let us apply this pattern to the lastName.

lastname: new FormControl('',[Validators.maxLength(15), Validators.pattern("^[a-zA-Z]+$")]),

Email Validator

This Validator requires that the control value must be a valid email address. We apply this to the email field.

email:new FormControl('',[Validators.email,Validators.required]),

Disable Submit button

We have successfully added the validators. Now, we need to disable the submit button if our form is not valid.

The Angular Forms API exposes the state of the forms through the FormGroupFormControl & FormArray instances. The FormGroup control has a property valid, which is set to true if all of its child controls are valid.

The contactForm represents the top-level FormGroup. We use it to set the disabled attribute of the submit button.

<button type="submit" [disabled]="!contactForm.valid">Submit</button>

Displaying the Validation/Error messages

We need to provide a short and meaningful error message to the user. We do that by using the error object returned by the FormControl instance. Every form element has a FormControl instance associated with it. It exposes the state of form element like validdirtytouched etc.

There are two ways in which you can get the reference to the FormControl. One way is to use the contactForm variable. We can use contactForm.controls.firstname.valid to find out if the firstname is valid.

 <div *ngIf="!firstname?.valid && (firstname?.dirty ||firstname?.touched)">
                    <div [hidden]="!firstname?.errors?.['required']">
                        First Name is required
                    </div>
                    <div [hidden]="!firstname?.errors?.['minlength']">
                        Min Length is 10
                    </div>
                </div>

Finial Reactive HTML Forms and Typescripts

Reactive Forms HTML

<div class="container">
    <form [formGroup]="contactForm" (ngSubmit)="onSubmit()" novalidate>
        <div class="row">
            <div class="col-md-2">
                <label for="firstname">First Name </label>
            </div>
            <div class="col-md-4">
                <input type="text" id="firstname" name="firstname" formControlName="firstname" class="form-control">
            </div>
            <div class="col-md-6 validation-style">
                <div *ngIf="!firstname?.valid && (firstname?.dirty ||firstname?.touched)">
                    <div [hidden]="!firstname?.errors?.['required']">
                        First Name is required
                    </div>
                    <div [hidden]="!firstname?.errors?.['minlength']">
                        Min Length is 10
                    </div>
                </div>
            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label for="lastname">Last Name </label>
            </div>
            <div class="col-md-4">
                <input type="text" id="lastname" name="lastname" formControlName="lastname" class="form-control">
            </div>
            <div class="col-md-6 validation-style">

                <div *ngIf="!lastname?.valid && (lastname?.dirty ||lastname?.touched)">
                    <div [hidden]="!lastname?.errors?.['pattern']">
                        Only characters are allowed
                    </div>
                    <div [hidden]="!lastname?.errors?.['maxLength']">
                        Max length allowed is {{lastname?.errors?.['maxlength']?.requiredLength}}
                    </div>
                    <div [hidden]="!lastname?.errors?.['required']">
                        Last Name is required
                    </div>
                </div>

            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label for="email">Email </label>
            </div>
            <div class="col-md-4">
                <input type="text" id="email" name="email" formControlName="email" class="form-control">
            </div>
            <div class="col-md-6 validation-style">
                <div *ngIf="!email?.valid && (email?.dirty ||email?.touched)">
                    <div [hidden]="!email?.errors?.['required']">
                        email is required
                    </div>
                    <div [hidden]="!email?.errors?.['email']">
                        invalid email id
                    </div>
                </div>
            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label for="gender">Geneder </label>
            </div>
            <div class="col-md-4">
                <input type="radio" value="male" id="gender" name="gender" formControlName="gender"> Male
                <input type="radio" value="female" id="gender" name="gender" formControlName="gender"> Female
                <input type="radio" value="transgender" id="gender" name="gender" formControlName="gender"> Trans Gender
            </div>
            <div class="col-md-6 validation-style">
                <div *ngIf="!gender?.valid && (gender?.dirty ||gender?.touched)">
                    <div [hidden]="!gender?.errors?.['required']">
                        gender is required
                    </div>
                </div>
            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label for="isMarried">Married </label>
            </div>
            <div class="col-md-4">
                <input type="checkbox" id="isMarried" name="isMarried" formControlName="isMarried">
            </div>
            <div class="col-md-6 validation-style">
                <div *ngIf="!isMarried?.valid && (isMarried?.dirty ||isMarried?.touched)">
                    <div [hidden]="!isMarried?.errors?.['required']">
                        isMarried is required
                    </div>
                </div>
            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label for="country">country </label>
            </div>
            <div class="col-md-4">
                <select id="country" name="country" formControlName="country" class="form-control">
                    <option [ngValue]="c.id" *ngFor="let c of countryList">
                        {{c.name}}
                    </option>
                </select>
            </div>
            <div class="col-md-6 validation-style">
                <div *ngIf="!country?.valid && (country?.dirty ||country?.touched)">
                    <div [hidden]="!country?.errors?.['required']">
                        country is required
                    </div>
                </div>
            </div>
        </div>

        <div formGroupName="address">
            <div class="form-group">
                <div class="row space-in-line">
                    <div class="col-md-2">
                        <label for="city">City</label>
                    </div>
                    <div class="col-md-4">
                        <input type="text" class="form-control" name="city" formControlName="city">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!city?.valid && (city?.dirty ||city?.touched)">
                            <div [hidden]="!city?.errors?.['required']">
                                city is required
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="form-group">
                <div class="row">
                    <div class="col-md-2">
                        <label for="street">Street</label>
                    </div>
                    <div class="col-md-4">
                        <input type="text" class="form-control" name="street" formControlName="street">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!street?.valid && (street?.dirty ||street?.touched)">
                            <div [hidden]="!street?.errors?.['required']">
                                street is required
                            </div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="form-group">
                <div class="row space-in-line">
                    <div class="col-md-2">
                        <label for="pincode">Pin Code</label>
                    </div>
                    <div class="col-md-4">
                        <input type="text" class="form-control" name="pincode" formControlName="pincode">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!pincode?.valid && (pincode?.dirty ||pincode?.touched)">
                            <div [hidden]="!pincode?.errors?.['required']">
                                pincode is required
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">
                <label> Is Form Valid</label>
            </div>
            <div class="col-md-4">
                {{contactForm.valid}}
            </div>
            <div class="col-md-6">

            </div>
        </div>

        <div class="row space-in-line">
            <div class="col-md-2">

            </div>
            <div class="col-md-4">
                <button type="submit" [disabled]="!contactForm.valid" class="btn btn-primary">Submit</button>
            </div>
            <div class="col-md-6">
            </div>
        </div>
    </form>
</div>

Reactive Form Typescript File

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-reactive-forms',
  templateUrl: './reactive-forms.component.html',
  styleUrls: ['./reactive-forms.component.css']
})
export class ReactiveFormsComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }
  contactForm = new FormGroup({
    firstname: new FormControl('',[Validators.required,Validators.minLength(10)]),
    lastname: new FormControl('',[Validators.required, Validators.maxLength(15), Validators.pattern("^[a-zA-Z]+$")]),
    email:new FormControl('',[Validators.email,Validators.required]),
    gender: new FormControl('',[Validators.required]),
    isMarried: new FormControl('',[Validators.required]),
    country: new FormControl('',[Validators.required]),
    address:new FormGroup({
      city: new FormControl('',[Validators.required]),
      street: new FormControl('',[Validators.required]),
      pincode:new FormControl('',[Validators.required])
    })
  })
 
  get firstname() {
    return this.contactForm.get('firstname');
  } 
 
  get lastname() {
    return this.contactForm.get('lastname');
  } 
 
  get email() {
    return this.contactForm.get('email');
  } 
 
  get gender() {
    return this.contactForm.get('gender');
  } 
 
  get isMarried() {
    return this.contactForm.get('isMarried');
  } 
 
  get country() {
    return this.contactForm.get('country');
  } 
 
  get city() {
    return  this.contactForm.get("address")?.get('city');
  } 
 
  get street() {
    return this.contactForm.get("address")?.get('street');
  } 
 
  get pincode() {
    return this.contactForm.get("address")?.get('pincode');
  } 

  countryList: country[] = [
    new country("1", "India"),
    new country('2', 'USA'),
    new country('3', 'England')
  ];
 
  onSubmit() {
    console.log(this.contactForm.value);
  }

}
export class contact {
  firstname?:string;
  lastname?:string;
  gender?:string;
  isMarried?:boolean;
  country?:string;
  address?: {
    city:string;
    street:string;
    pincode:string;
  }
} 
 
export class country {
  id: string;
  name: string;
 
  constructor(id: string, name: string) {
    this.id = id;
    this.name = name;
  }
}
 

Output

When we run the application we can see the output like below, where we see how validation is work on the form and how all the form elements are captured in the console window.

Reactive Forms Validation

#Find Source Code

Conclusion

In this lesson we will learn about how Angular Reactive Forms Validation works. One of the common tasks that is performed, while building a form is Validation. Here we will learn how to build Reactive Forms and apply Built-in validators.

Leave behind your valuable queries, suggestions in the comment section below. Also, if you think this article help to you, do not forget to share this within your developer community. Happy Coding 🙂

Jayant Tripathy
Coder, Blogger, YouTuber

A passionate developer keep focus on learning and working on new technology.


Leave a Reply

Your email address will not be published.