Menu Close

Angular Template Driven Forms Validation


In this article we will learn about Angular Template Driven Forms Validation. Validations in Template-driven forms are provided by the Validation directives. The Angular Forms Module comes with several built-in validators. You can also create your own custom Validator. Please read my previous lesson Template driven forms in Angular.

#Find Source Code

You can find the demo on stackblitz on below,

Template Driven Forms

Consider the following template-driven form. It has firstnamelastnameemailgender & istoc form fields.

<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)">
  <p>
    <label for="firstname">First Name </label>
    <input type="text" id="firstname" name="firstname" [(ngModel)]="contact.firstname">
  </p>
  <p>
    <label for="lastname">Last Name </label>
    <input type="text" id="lastname" name="lastname" [(ngModel)]="contact.lastname">
  </p>
  <p>
    <label for="email">email </label>
    <input type="text" id="email" name="email" [(ngModel)]="contact.email">
  </p>
  <p>
    <label for="gender">Geneder </label>
    <input type="radio" value="male" id="gender" name="gender" [(ngModel)]="contact.gender"> Male
    <input type="radio" value="female" id="gender" name="gender" [(ngModel)]="contact.gender"> Female
  </p>
  <p>
    <label for="isToc">Accept TOC</label>
    <input type="checkbox" id="isToc" name="isToc" [(ngModel)]="contact.isToc">
  </p>
  <p>
    <button type="submit">Submit</button>
  </p>
</form>

Component Class

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit  {
  title = 'Template driven forms';
 
  @ViewChild('contactForm',null) contactForm: NgForm;
  contact:contact;
  ngOnInit() {
    this.contact = { 
      firstname:"",
      lastname:"",
      gender:"male",
      isToc:true,
      email:"",
    };
  }
  onSubmit() {
    console.log(this.contactForm.value);
  }
}
export class contact {
  firstname:string;
  lastname:string;
  gender:string;
  isToc:boolean;
  email:string;
}

Disabling the Browser validation

First, we need to disable browser validator interfering with the Angular validator. To do that we need to add novalidate attribute on element as shown below.

<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)" novalidate>

Built-in Validators

The Built-in validators use the HTML5 validation attributes like requiredminlengthmaxlength & pattern. Angular interprets these validation attributes and add the validator functions to FormControl instance.

Disable Submit button

Now, we have successfully added the validators. You will notice that the click submit button still submits the form.

We need to disable the submit button if our form is not valid.

Angular forms module keep track of the state of our form and each of its form elements. These states are exposed to the user through FormGroupFormArray & FormControl objects.

We get the reference to the top-level FormGroup instance by creating a template variable and bind it to ngForm. We have already done it when we had added the #contactForm="ngForm" in our form tag.

The FormGroup has a valid property, which is set to true if all of its child controls are valid. We use it to set the disabled attribute of the submit button.

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

Displaying the Validation/Error messages

We need to provide a short and meaningful error message to the user. Angular creates a FormControl for each and every field, which has ngModel directive applied. The FormControl exposes the state of form element like valid, dirty, touched, 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 the contactForm.controls.firstname.valid to find out if the firstname is valid.

The other way is to create a new local variable for each FormControl For Example, the following firstname=”ngModel” creates the firstname variable with the FormControl instance.

<input type="text" id="firstname" name="firstname" required minlength="10" 
            #firstname="ngModel" [(ngModel)]="contact.firstname">

Now, we have a reference to the firstname FormControl instance, we can check its status. We use the valid property to check if the firstname has any errors. valid: returns either invalid status or null which means a valid status

<div *ngIf="!firstname?.valid && (firstname?.dirty || firstname?.touched)" class="error">
     <div *ngIf="firstname.errors?.['required']"> First Name is required</div>
       <div *ngIf="firstname?.errors?.['minlength']">
          First Name Minimum Length is {{firstname?.errors?.['minlength']?.requiredLength}}
       </div>
  </div>

Why check dirty and touched?

We do not want the application to display the error when the form is displayed for the first time. We want to display errors only after the user has attempted to change the value. The dirty & touched properties help us do that.

dirty: A control is dirty if the user has changed the value in the UI.
touched: A control is touched if the user has triggered a blur event on it.

Error message

The error message ” “Invalid First Name” ” is not helpful. The firstname has two validators. required and minlength

Any errors generated by the failing validation is updated in the errors object. The errors object returns the error object or null if there are no errors.

<div *ngIf="!firstname?.valid && (firstname?.dirty || firstname?.touched)">
  Invalid First Name
  <div *ngIf="firstname?.errors?.['required']">
     First Name is required
  </div>
  <div *ngIf="firstname?.errors?.['minlength']">
    First Name Minimum Length is {{firstname?.errors?.['minlength']?.requiredLength}}
  </div>
</div>

Final HTML Template Driven Form

<div class="container">
    <div class="panel panel-default">
        <div class="panel-body">
            <form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)" 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" required minlength="10" #firstname="ngModel"
                            [(ngModel)]="contact.firstname">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!firstname?.valid && (firstname?.dirty || firstname?.touched)" class="error">
                            <div *ngIf="firstname.errors?.['required']">
                                First Name is required
                            </div>
                            <div *ngIf="firstname?.errors?.['minlength']">
                                First Name Minimum Length is {{firstname?.errors?.['minlength']?.requiredLength}}
                            </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" required maxlength="15" #lastname="ngModel"
                            pattern="^[a-zA-Z]+$" [(ngModel)]="contact.lastname">
                    </div>
                    <div class="col-md-6 validation-style">

                        <div *ngIf="!lastname?.valid && (lastname?.dirty || lastname?.touched)" class="error">
                            <div *ngIf="lastname?.errors?.['required']">
                                Last Name is required
                            </div>
                            <div *ngIf="lastname?.errors?.['maxlength']">
                                Last Name Minimum Length is {{lastname?.errors?.['maxlength']?.requiredLength}}
                            </div>
                            <div *ngIf="lastname?.errors?.['pattern']">
                                Only characters are allowed
                            </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" required email #email="ngModel"
                            [(ngModel)]="contact.email">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!email?.valid && (email?.dirty || email?.touched)" class="error">
                            <div *ngIf="email?.errors?.['required']">
                                Email is required
                            </div>
                            <div *ngIf="email?.errors?.['email']">
                                Invalid Email Address
                            </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" #gender="ngModel" required
                            [(ngModel)]="contact.gender">Male
                        <input type="radio" value="female" id="gender" name="gender" #gender="ngModel" required
                            [(ngModel)]="contact.gender"> Female
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!gender?.valid && (gender?.dirty || gender?.touched)" class="error">
                            <div *ngIf="gender?.errors?.['required']">
                                Gender is required
                            </div>
                        </div>
                    </div>
                </div>

                <div class="row space-in-line">
                    <div class="col-md-2">
                        <label for="isToc">Accept Term and Conditions</label>
                    </div>
                    <div class="col-md-4">
                        <input type="checkbox" id="isToc" name="isToc" required #isToc="ngModel"
                            [(ngModel)]="contact.isToc">
                    </div>
                    <div class="col-md-6 validation-style">
                        <div *ngIf="!isToc?.valid && (isToc?.dirty || isToc?.touched)" class="error">
                            <div *ngIf="isToc?.errors?.['required']">
                                Please accept the TOC
                            </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>
    </div>
</div>

Final Template Driven Typescript

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-template-driven-forms',
  templateUrl: './template-driven-forms.component.html',
  styleUrls: ['./template-driven-forms.component.css']
})
export class TemplateDrivenFormsComponent implements OnInit {
  contact: Contact={};
  constructor() { }

  ngOnInit(): void {
    this.contact = {
      firstname: '',
      lastname: '',
      gender: 'male',
      isToc: true,
      email: ''
      };
  }
  onSubmit(contactForm: any) {
    console.log(contactForm.value);
    }
}
export class Contact {
  firstname?: string;
  lastname?: string;
  gender?: string;
  isToc?: boolean;
  email?: string;
  }

Output

Template Driven Forms Validation

#Find Source Code

Conclusion

In this article we will learn about Angular Template Driven Forms Validation. Validations in Template-driven forms are provided by the Validation directives. The Angular Forms Module comes with several 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.