Reactive and Template Driven Forms in Angular
By: Taner Şahin
Tags: Software Development, Angular, Google, Front End Development, Typescript

Angular Forms

Angular is a very popular front-end framework created by Google. Just like other popular frameworks, it uses a component-based architecture. In this article, we’ll look at the basics of creating forms with Angular.

Different Kinds of Forms

Angular providing 2 different approaches to handling user input through forms.

They are reactive and template-driven forms. They both capture events from the view, validate their user input, and create a form model and data model to update, and provide a way to track changes.

Template Driven Forms

Easy to use

Suitable for simple scenarios

Fails for complex scenarios

Using two-way data binding

Lesser component code

Automatic track of the form and it’s data (Angular is handling them)

Unit testing is a challenge

Reactive Forms

More flexible but needs a lot of practice

Handles any kind of complex scenarios

More component code and less HTML markup

Unit testing is easy

Template-driven form with validation Angular example

Importing FormsModule

You need to import FormsModule into the AppModule (app.module.ts) for the Angular template-driven form to work. When you import the FormsModule in your component, Angular automatically creates and attaches a NgForm directive to the <form> tag in the template. Using NgForm you can track form value and validation status.

Add FormsModule in the imports section of the AppModule.

imports: [

BrowserModule,

FormsModule

],

Data Model (member.model.ts)

Member class defines the data model reflected in the form.

export class Member {

name: string;

mail: string;

membershipDate: Date;

}

Form Template (app.component.html)

In this Angular template driven form example we’ll have a form with two input fields for name and email, a date picker for picking date

<div class=”container”>

<div class=”row”>

<div class=”col-xs-12 col-sm-10 col-md-8">

<h1>Membership Form</h1>

<form (ngSubmit)=”onSubmit()” #membershipForm=”ngForm”>

<div class=”form-group”>

<label for=”name”>Name</label>

<input type=”text” class=”form-control”

id=”name”

ngModel name=”name” #name=”ngModel” required>

<div class=”alert alert-danger” *ngIf=”name.invalid && name.touched”>Name is required</div>

</div>

<div class=”form-group”>

<label for=”email”>email</label>

<input type=”email” class=”form-control”

id=”email” required email

ngModel name=”email” #email=”ngModel”>

<div class=”alert alert-danger” *ngIf=”email.invalid && email.touched”>

Please enter a valid email

</div>

</div>

<div class=”form-group”>

<label for=”mdate”>Membership Date</label>

<input type=”date” class=”form-control” id=”mdate”

[ngModel]=”currentDate | date:’yyyy-MM-dd’” name=”mdate”>

</div>

<button type=”submit” [disabled]=”membershipForm.invalid” class=”btn btn-success”>Submit</button>

</form>

</div>

</div>

<hr>

<div *ngIf=”submitted”>

<div class=”row”>

<div class=”col-xs-12 col-sm-10 col-md-8">

<p>Name: {{member.name}}</p>

<p>email: {{member.mail}}</p>

<p>Membership Date: {{member.membershipDate | date:’dd/MM/yyyy’}}</p>

</div>

</div>

</div>

</div>

Important points to note

1) If you realized that ngModel is added with every element. It tells Angular that this element is a form of control. Just Membership date ngvalue Model is used with square brackets meaning as a one way binding to get the current date.

2) For name, email, and type HTML5 required attribute has been added to specify that a value is required. With email control, email attribute has also been added to specify that the entered value has to be a valid email. You can check the whole list of available Angular validators from here: https://angular.io/api/forms/Validators

3) Another important point is that the Angular framework doesn’t directly use HTML5 validator attributes. When it detects an HTML 5 validator attribute Angular adds a directive to that attribute. For example, if Angular detects a control marked with the email attribute, it adds its own EmailValidator directive.

4) Error message that is shown beneath the form controls is done by adding a <div> with the form control where message is needed. For example if you want to show error message for name control.

<div class=”alert alert-danger” *ngIf=”name.invalid && name.touched”>Name is required</div>

Here alert and alert-danger are Bootstrap classes. Message is displayed if the control has been visited and name is not entered which is taken care of by *ngIf=”name.invalid && name.touched”.

5) Validation classes are added at the form level too apart from with each form control to determine whether the form as a whole is valid or not. Using that you can disable the submit button and enable it only when the whole form is valid. That’s what [disabled]=”membershipForm.invalid” does in the submit button.

CSS class (app.component.css)

Also need to add a CSS class to show a bar on the left side of the control.

.ng-invalid.ng-untouched:not(form) {

border-left: 5px solid #42A948; /* green */

}

.ng-invalid.ng-touched:not(form) {

border-left: 5px solid #a94442; /* red */

}

ng-invalid. ng-untouched: not(form) means select all elements having both ng-invalid and ng-untouched set within its class attribute and it is not at the form level. If you won’t add (form) you will have a bar at the form level too encompassing all controls.

Same way .ng-invalid.ng-touched: not(form) means select all elements having both ng-invalid and ng-touched set within its class attribute and it is not at form level.

Component class (app.component.ts)

import { NgForm } from ‘@angular/forms’;

import { Member } from ‘./member.model’;

@Component({

selector: ‘app-root’,

templateUrl: ‘./app.component.html’

})

export class AppComponent {

@ViewChild(‘membershipForm’) memberForm : NgForm;

currentDate = new Date();

member = new Member(‘’, ‘’, new Date(), ‘’);

submitted = false;

onSubmit(){

this.submitted = true;

this.member.name = this.memberForm.value.name;

this.member.mail = this.memberForm.value.email;

this.member.membershipDate = this.memberForm.value.mdate;

} }

@ViewChild decorator is used here to access the local reference. Since local reference membershipForm holds a reference to the JS object for the form so this way you can get access to the form object in your typescript code.

Now you create a member object of the Member class where you will assign the field values when the onSubmit() is called.

Reactive Approach

Before start need to explain a few concepts

formGroup: The form will be treated as a FormGroup in the component class, so the formGroup directive allows to give a name to the form group.

ngSubmit: This is the event that will be triggered upon submission of the form.

formControlName: Each form field should have a formControlName directive with a value that will be the name used in the component class.

Example

A simple login form that using a reactive forms approach

app.component.ts

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

import { FormGroup, FormBuilder } from ‘@angular/forms’;

@Component({

selector: ‘app-root’,

templateUrl: ‘./app.component.html’,

styleUrls: [‘./app.component.css’]

})

export class AppComponent implements OnInit {

angForm: FormGroup;

constructor(private fb: FormBuilder) {

this.createForm();

}

createForm() {

this.angForm = this.fb.group({

email: [‘’],

password: [‘’]

});

}

onClickSubmit(email, password) {

alert(‘Your Email is : ‘ + email);

}

}

app.component.html

<form [formGroup]=”angForm”>

<input type = “text”

name = “email”

placeholder = “email”

formControlName=”email”

#email>

<br/>

<input type = “password”

name = “pwd”

placeholder = “password”

formControlName=”password”

#password>

<br/>

<input type = “submit”

value = “submit”

(click) = “onClickSubmit(email.value, password.value)” >

</form>

How to create form dynamically using the Reactive Approach?

Example.html

<div [formGroup]=”inviteForm”>

<div formArrayName=”items” *ngFor=”let item of inviteForm.get(‘items’)[‘controls’]; let i = index;”>

<div [formGroupName]=”i”>

<mat-form-field class=”example-full-width” style=”width:60%”>

<mat-label>UserMailAdress</mat-label>

<input formControlName=”mailAdress” matInput placeholder=”EMail”>

</mat-form-field>





<mat-form-field appearance=”fill” style=”width:35%”>

<mat-label>UserType</mat-label>

<mat-select formControlName=”userType”>

<mat-option selected=”selected” value=”0" >Admin</mat-option>

<mat-option value=”1">Agent’ </mat-option>

<mat-option value=”2">SuperUser</mat-option>

</mat-select>

</mat-form-field>

</div>

</div>

</div>

Example.ts

public inviteForm: FormGroup;

public items: FormArray;



public constructor(private formBuilder: FormBuilder) {

this.inviteForm = this.formBuilder.group({

items: this.formBuilder.array([this.createItem()])

});

}



public createItem(): FormGroup {

return this.formBuilder.group({

mailAdress: ‘’,

userType: 0

});

}

public addItem(): void {

this.items = this.inviteForm.get(‘items’) as FormArray;

this.items.push(this.createItem());

}

public removeItem(): void {

this.items = this.inviteForm.get(‘items’) as FormArray;

const lastValue = this.items.length — 1;

this.items.removeAt(lastValue);

}