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.
You can find the demo on stackblitz on below,
Template Driven Forms
Consider the following template-driven form. It has firstname
, lastname
, email
, gender
& 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 required
, minlength
, maxlength
& 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 FormGroup
, FormArray
& 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
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 and suggestions in the comment section below. Also, if you think this article helps you, do not forget to share this with your developer community. Happy Coding 🙂
Jayant Tripathy
Coder, Blogger, YouTuberA passionate developer keep focus on learning and working on new technology.