This article will give you extensive information on Template Driven Forms and how to create a form with Template Driven Forms approach, as well as how to implement validation with Template Driven Forms with Angular 6 and TypeScript.
Forms are useful when you need to gather information from the user. They provide an interface from where a user can fill in all required details and validate the information before submitting the data. Validation is not only required for checking the required data but also to use for checking patterns of data. When talking about form and validation in Angular, we have two different ways to create forms, the first one is the Template Driven Forms and another one is Reactive Forms. Today we only focus on Template Driven Form and learn what it is and why we should use it. Apart from this, we will also learn how to create Template Driven Forms along with validations.
So, let's first understand what Template Driven Forms are and what are the reasons to use them.
TEMPLATE DRIVEN FORMS
As the name suggests, in this approach to create forms, we focus on template instead of component code. We add the template with different types of controls like input, dropdown, checkbox etc., and bind directive with them to handle the behavior of template. It is not a new approach, however, we have also used this approach in Angular JS. This is a similar way to create a form using ngModel and two-way data-binding as we did in Angular JS. In Template Driven Forms, validations are implemented using HTML 5 attributes like required, minLength, maxLength, pattern etc. This is not very complex while implementing, it is very easy to use. We cannot use Template Driven Forms approach to create forms in each scenario, this is only feasible for those scenarios where form structure is simple.
If you are willing to write test cases for the app then don't use this approach, since with the Template Driven Forms, testing the form is not easy. Another point I would like to mention here is that due to so many codes in Template sides, it creates the complexity to understand template codes and create readability issues.
CREATE ANGULAR PROJECT
Now let's move forward to a practical demonstration and step by step understanding of how to create Template Driven Forms and implement validation with that. So, we are going to create an Angular project [For this demonstration, we are using Angular version 6]. You can follow the below steps to create a new Angular 6 CLI project.
- Open Visual Studio Code and press CTRL + ~ to open a terminal window.
- Move to the particular directory where the project needs to be created.
- To create a new Angular CLI project, type command ng new FormValidationDemo --routing and press Enter.
- Execute command npm install bootstrap@3 to add bootstrap with the project.
- Now we have project ready, now move to that project folder and run the project using a command as ng serve --open
For more about how to create an Angular CLI project with step by step instructions, please follow this article,
MORE ARTICLES ON ANGULAR WHICH YOU MAY LIKE.
IMPLEMENTATION OF TEMPLATE DRIVEN FORMS
Before moving to the next step, first, we will configure Bootstrap, so that we can use the bootstrap classes. So, open styles.css and add the following code which is responsible for importing bootstrap CSS from installed node packages.
- @import "node_modules/bootstrap/dist/css/bootstrap.min.css";
- .invalid-data {
- border: 2px solid red;
- }
-
- .valid-data {
- border: 2px solid rgb(19, 92, 4);
- }
To implement the Template Driven Forms with an Angular project, we first need to add a module which is essential for providing all the required components of the Template Driven Form. So, open the app.mdoule.ts and add the FormsModule imported from @angular/forms. A point to note here is that FormsModule is used for creating Template Driven Forms and ReactiveFormsModule is used for created Reactive Forms.
After that, we will add one component as the name 'TemplateDrivenFormComponent' where we will create a form and validation with that. To add a new component, move to app folder in terminal windows and type the command as follows.
ng g c TemplateDrivenForm
So, now we have component ready. Let's confirm the route to reach with this component.
- import { NgModule } from '@angular/core';
- import { Routes, RouterModule } from '@angular/router';
- import { TemplateDrivenFormComponent } from 'src/app/template-driven-form/template-driven-form.component';
-
- const routes: Routes = [
- {
- path: 'templateform',
- pathMatch: 'full',
- component: TemplateDrivenFormComponent
- }
- ];
-
- @NgModule({
- imports: [RouterModule.forRoot(routes)],
- exports: [RouterModule]
- })
- export class AppRoutingModule { }
Now, let's create a simple form using bootstrap classes where the user can enter his/her address details. So, just open the 'template-driven-form.component.html' and add the following HTML code.
- <div class="row">
- <div class="col-md-4 col-md-offset-4" style="margin-top: 50px; border: 1px solid rgb(100, 98, 98); padding: 30px;">
- <form class="form-horizontal" role="form">
- <fieldset>
- <legend>Address Details:
- <strong>Template Driven Form</strong>
- </legend>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">Address</label>
- <div class="col-sm-10">
- <input type="text" name="address" placeholder="Enter Address" class="form-control">
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">City</label>
- <div class="col-sm-10">
- <input type="text" name="city" placeholder="Enter City Name" class="form-control">
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">State</label>
- <div class="col-sm-4">
- <input type="text" name="state" placeholder="State" class="form-control">
- </div>
-
- <label class="col-sm-2 control-label" for="textinput">Postcode</label>
- <div class="col-sm-4">
- <input type="text" name="postcode" placeholder="Enter Post Code" class="form-control">
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">Country</label>
- <div class="col-sm-10">
- <select class="form-control" name="country">
- <option>---Select---</option>
- <option *ngFor="let item of countryData" [value]="item">
- {{item}}
- </option>
- </select>
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-sm-2 form-check">
- <input type="checkbox" name="aggrement" class="form-check-input">
- </div>
- <label class="col-sm-10 form-check-label">I aggree to Terms & Conditions
- </label>
- </div>
-
- <div class="form-group">
- <div class="col-sm-12">
- <span style="color: red;">Please Aggree with Terms & Conditions.</span>
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-sm-offset-2 col-sm-10">
- <div class="pull-right">
- <button type="submit" class="btn btn-primary" style="margin: 4px;">Save</button>
- <button type="reset" class="btn btn-default">Reset</button>
- </div>
- </div>
- </div>
- </fieldset>
- </form>
- </div>
- </div>
First, let us see what will be the output of the above template. So, run the app usig ng serve command. Now see the output. You will find the output as follows. Here, we are going to create an Address form which has an address, city, state, and postcode as an Input field and country as a drop-down field. Apart from this, we have one checkbox for validating the terms and conditions. We will try to validate almost every control, either input control or dropdown. Form validation will happen on the form's submission.
The above form is a simple form and before converting the above simple form to Template Driven Forms, we will create a Model as follows.
- export interface addressModel{
- address: string,
- city: string,
- state: string,
- postcode: number,
- country: any[],
- aggrement: boolean
- }
Now, let's modify the 'template-driven-form.component.ts' file as follows. Here we first construct the data for Country drop-down and construct a model with a type of addressModel with their default values. We have also defined one method as onFormSubmit() which will be called when we will submit the form. We are using console.log() for now to print the data in console window of the browser.
- import { Component, OnInit } from '@angular/core';
- import { addressModel } from 'src/app/data/address.model';
-
- @Component({
- selector: 'app-template-driven-form',
- templateUrl: './template-driven-form.component.html',
- styleUrls: ['./template-driven-form.component.css']
- })
- export class TemplateDrivenFormComponent implements OnInit {
-
- countryData: any[] = ['India', 'US', 'UK'];
-
- model: addressModel = {
- address: '',
- city: '',
- state:'',
- postcode: null,
- country: null,
- aggrement: false
- };
-
- constructor() { }
-
- ngOnInit() {
- }
-
- onFormSubmit() {
- console.log("Full Address", this.model);
- }
- }
Now, its time to understand what are the attributes available from HTML 5 which can be used to validate the forms and these attributes are as follows.
- required - Value is required.
- minlength - The minimum characters are required as a value.
- maxlength - The maximum characters are required as a value.
- pattern - The value should match the pattern.
In Angular, we can validate HTML controls as well as forms. There are different attributes available to validate form's control which return true or false.
- untouched - The control is not touched yet.
- touched - The control is touched.
- pristine - The control's value is not modified or changed yet.
- dirty - The control's value is modified or changed.
- invalid - The control's value is not valid.
- valid - The control's value is valid.
We can validate Form also using the following attributes.
- pristine - Not any control's value has modified yet.
- dirty - Some or all control's values have modified yet.
- invalid - Form is not valid since control value is not valid.
- valid - Form is valid.
- submitted - The form has submitted.
Now, let's change the above template one by one to implement Template Driven Forms along with validation.
First, let's modify the form tag as follows, here you can see we are creating the instance of ngForm directive and defining ngSubmit event, which will call onFormSubmit() method once form will be valid.
- <form class="form-horizontal" role="form" #f="ngForm" (ngSubmit)="f.form.valid && onFormSubmit()">
As per the Angular.IO.
The NgForm directive supplements the form element with additional features. It holds the controls you created for the elements with an ngModel directive and name attribute, and monitors their properties, including their validity. It also has its own valid property which is true only if every contained control is valid.
The #f is the reference variable to the form directive. Using this, we can handle the behavior of the forms.
Now let's modify the template for Input control. First, we will use [(ngModel)] to use two-way data binding with model data. After that, we will add CSS class which changes the color of the control on being valid or invalid while submitting the form. Next, add the HTML 5 attribute required to make value compulsory for that control. Next is a very important point, here we will add ngModel directive with the name of the control along with the prefix of #.
Here #address="ngModel" exports NgModel into a local variable called name. NgModel mirrors many of the properties of its underlying FormControl instance, so you can use this in the template to check for control states such as valid and dirty. You can also add validation using touched or untouched.
- <input type="text" [(ngModel)]="model.address" [ngClass]="{'invalid-data': address.invalid && f.submitted, 'valid-data': address.valid && f.submitted}"
- required name="address" #address="ngModel" placeholder="Enter Address" class="form-control">
-
- <div *ngIf="address.invalid && f.submitted">
- <span style="color: red;">Please Enter Address.</span>
- </div
As per the above guidelines which we have implemented with the Input control, we will follow for Dropdown and Checkbox as well respectively.
- <div class="col-sm-10">
- <select class="form-control" name="country" [(ngModel)]="model.country" #country="ngModel" [ngClass]="{'invalid-data': country.invalid && f.submitted, 'valid-data': country.valid && f.submitted}" required>
- <option [ngValue]="null">---Select---</option>
- <option *ngFor="let item of countryData" [value]="item">
- {{item}}
- </option>
- </select>
-
- <div *ngIf="country.invalid && f.submitted">
- <span style="color: red;">Please Select Country.</span>
- </div>
- </div>
Checkbox validation is as follows.
- <div class="form-group">
- <div class="col-sm-2 form-check">
- <input type="checkbox" [(ngModel)]="model.aggrement" [ngClass]="{'invalid-data': aggrement.invalid && f.submitted, 'valid-data': aggrement.valid && f.submitted}" required name="aggrement" #aggrement="ngModel" class="form-check-input">
- </div>
- <label class="col-sm-10 form-check-label">I aggree to Terms & Conditions
- </label>
- </div>
We can change form code similar to the following code to make it Template Driven Forms along with validation implementations.
- <div class="row">
- <div class="col-md-4 col-md-offset-4" style="margin-top: 50px; border: 1px solid rgb(100, 98, 98); padding: 30px;">
- <form class="form-horizontal" role="form" #f="ngForm" (ngSubmit)="f.form.valid && onFormSubmit()">
- <fieldset>
- <legend>Address Details:
- <strong>Template Driven Form</strong>
- </legend>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">Address</label>
- <div class="col-sm-10">
- <input type="text" [(ngModel)]="model.address" [ngClass]="{'invalid-data': address.invalid && f.submitted, 'valid-data': address.valid && f.submitted}"
- required name="address" #address="ngModel" placeholder="Enter Address" class="form-control">
-
- <div *ngIf="address.invalid && f.submitted">
- <span style="color: red;">Please Enter Address.</span>
- </div>
-
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">City</label>
- <div class="col-sm-10">
- <input type="text" [(ngModel)]="model.city" [ngClass]="{'invalid-data': city.invalid && f.submitted, 'valid-data': city.valid && f.submitted}"
- required name="city" #city="ngModel" placeholder="Enter City Name" class="form-control">
-
- <div *ngIf="city.invalid && f.submitted">
- <span style="color: red;">Please Enter City.</span>
- </div>
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">State</label>
- <div class="col-sm-4">
- <input type="text" [(ngModel)]="model.state" name="state" placeholder="State" class="form-control">
- </div>
-
- <label class="col-sm-2 control-label" for="textinput">Postcode</label>
- <div class="col-sm-4">
- <input type="text" [(ngModel)]="model.postcode" [ngClass]="{'invalid-data': postcode.invalid && f.submitted, 'valid-data': postcode.valid && f.submitted}"
- required name="postcode" #postcode="ngModel" placeholder="Enter Post Code" class="form-control">
-
- <div *ngIf="postcode.invalid && f.submitted">
- <span style="color: red;">Please Enter Valid Post Code.</span>
- </div>
- </div>
- </div>
-
- <div class="form-group">
- <label class="col-sm-2 control-label" for="textinput">Country</label>
- <div class="col-sm-10">
- <select class="form-control" name="country" [(ngModel)]="model.country" #country="ngModel" [ngClass]="{'invalid-data': country.invalid && f.submitted, 'valid-data': country.valid && f.submitted}"
- required>
- <option [ngValue]="null">---Select---</option>
- <option *ngFor="let item of countryData" [value]="item">
- {{item}}
- </option>
- </select>
-
- <div *ngIf="country.invalid && f.submitted">
- <span style="color: red;">Please Select Country.</span>
- </div>
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-sm-2 form-check">
- <input type="checkbox" [(ngModel)]="model.aggrement" [ngClass]="{'invalid-data': aggrement.invalid && f.submitted, 'valid-data': aggrement.valid && f.submitted}"
- required name="aggrement" #aggrement="ngModel" class="form-check-input">
- </div>
- <label class="col-sm-10 form-check-label">I aggree to Terms & Conditions
- </label>
- </div>
-
- <div class="form-group">
- <div class="col-sm-12" *ngIf="aggrement.invalid && f.submitted">
- <span style="color: red;">Please Aggree with Terms & Conditions.</span>
- </div>
- </div>
-
- <div class="form-group">
- <div class="col-sm-offset-2 col-sm-10">
- <div class="pull-right">
- <button type="submit" class="btn btn-primary" style="margin: 4px;">Save</button>
- <button type="reset" class="btn btn-default">Reset</button>
- </div>
- </div>
- </div>
- </fieldset>
- </form>
- </div>
- </div>
So, we have finished converting a simple form to Template Driven Forms along with validation with each control. Let's run the project and see the output. Once we will run the project and click to Save button, all required field borders will be in red. If we fill in the valid value for that control, it will become green.
Conclusion
So, today we learned about Template Driven Forms in Angular and how to create and implement validation with them using TypeScript.
I hope this post will help you. Please put your feedback using comment which helps me to improve myself for next post. If you have any doubts please ask your doubts or query in the comment section and If you like this post, please share it with your friends. Thanks.