Model driven or Reactive forms provides reactive style of programming. These forms explicitly manages following of data between a non-UI data model (retrieved from server) and a UI-oriented form model capable of retaining the states and values of the HTML controls on screen. Reactive Forms find its usage in case of reactive patterns, testing and validation. Reactive forms belong to @angular/forms library within ReactiveFormsModule.
In reactive forms component class will consist of a tree of angular form control objects which is then binded to component template native form control elements as a result all controls are always available and their value can be immediately updated if changes are made. These forms are synchronous. One might not think that if a tree of form control objects is created in component code then html for form will be automatically created for us instead html for form controls is to be created and then html form controls are linked to code in the component explicitly.
Consider following example of Model Driven Form :-
Create a Component "ModelFormComponent" using command "ng g c ModelForm" creating a folder with name "model-form" containing component, component template and component style files.
Component code is in "model-form.component.ts" and component template is "model-form.component.html".
Parent Component is "AppComponent".
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ModelFormComponent } from './model-form/model-form.component';
import { ReactiveFormsModule} from '@angular/forms'; //Include ReactiveFormsModule for using reactive forms
@NgModule({
declarations: [
AppComponent,
ModelFormComponent
],
imports: [
BrowserModule,
ReactiveFormsModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
model-form.component.ts
This file consist of form model. Instances of FormGroups and FormControls make up a form model in component. FormControls instances are nested inside FormGroups. FormGroups can also be nested inside another FormGroups.
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-model-form',
templateUrl: './model-form.component.html',
styleUrls: ['./model-form.component.css']
})
export class ModelFormComponent implements OnInit {
userform: FormGroup; //instance of FormGroup representing form
constructor() { }
ngOnInit() {
// tree of form control objects
this.userform = new FormGroup({
name: new FormGroup({
firstName: new FormControl(), //instance of FormControl representing a control inside FormGroup
lastName: new FormControl(),
}),
email: new FormControl(),
password: new FormControl(),
});
}
}
model-form.component.html
It links the component form model to the component form template. In aboce code "userform" form model is binded to <form> element using formGroup directive. This will only link form element to the userform model but does not link model form control to each form control in the template. This linking is performed explicitly via formControlName and formGroupName directives. Form values can be seen via <pre>{{userform.value | json}}</pre>, which are now binded to "userform" model in component. The template can be as follows :-
<div class="container">
<form [formGroup]="userform" novalidate>
<fieldset formGroupName="name">
<div class="form-group">
<label>First Name</label>
<input type="text" class="form-control" formControlName="firstName"/>
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" formControlName="lastName">
</div>
</fieldset>
<div class="form-group">
<label>Email</label>
<input type="email" class="form-control" formControlName="email">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" formControlName="password">
</div>
<div><pre>{{userform.value | json}}</pre></div>
</form>
</div>
app.component.ts
import { Component} from '@angular/core';
@Component({
selector: 'app-root',
template: '<app-model-form></app-model-form>'
})
export class AppComponent{
constructor () { }
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularDemo</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
</head>
<body>
<app-root></app-root>
</body>
</html>
When we run the application using "ng serve --open" following screen is obtained
![](http://findnerd.s3.amazonaws.com/imagedata/9314/9314.png)
Changing values in input boxes also changes form model values
![](http://findnerd.s3.amazonaws.com/imagedata/9316/9316.png)
Model Driven Form Validation
Adding of validation checks in component form template through component form model. Input controls need to follow these validation checks if not followed then control is treated as invalid. There are pre-built validators like required, minlegth, maxlength and pattern which can be accessed via Validators module. ModelFormComponent consisted of a tree of form control objects. FormControl was used to create a control inside FormGroup. Validators can be applied to a control through FormControl constructor. The first parameter of a FormControl constructor is control initial value while the second parameter is for a single or list of validators applied to the control.
Example :
Following files are changed :-
model-form.component.ts
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl,Validators } from '@angular/forms';
@Component({
selector: 'app-model-form',
templateUrl: './model-form.component.html',
styleUrls: ['./model-form.component.css']
})
export class ModelFormComponent implements OnInit {
userform: FormGroup;
constructor() { }
ngOnInit() {
this.userform = new FormGroup({
name: new FormGroup({
firstName: new FormControl("",Validators.required), //single validator
lastName: new FormControl("",Validators.required),
}),
email: new FormControl("",[Validators.required,Validators.pattern("[^ @]*@[^ @]*")]), //list of validators for control
password: new FormControl("",[Validators.required,Validators.minLength(8)]),
});
}
}
model-form.component.html
<div class="container">
<h3 class="jumbotron text-centre">User Form</h3>
<form [formGroup]="userform" novalidate>
<fieldset formGroupName="name">
<div class="form-group" [ngClass]="{'has-danger': (userform.controls.name.controls.firstName.invalid && userform.controls.name.controls.firstName.touched)}">
<label>First Name</label>
<input type="text" class="form-control" formControlName="firstName" />
<div class="form-control-feedback" *ngIf="userform.controls.name.controls.firstName.errors && userform.controls.name.controls.firstName.touched">
<p class="text-danger" *ngIf="userform.controls.name.controls.firstName.errors.required">FirstName is required</p>
</div>
</div>
<div class="form-group" [ngClass]="{'has-danger': (userform.controls.name.controls.lastName.invalid && userform.controls.name.controls.lastName.touched)}">
<label>Last Name</label>
<input type="text" class="form-control" formControlName="lastName">
<div class="form-control-feedback" *ngIf="userform.controls.name.controls.lastName.errors && userform.controls.name.controls.lastName.touched">
<p class="text-danger" *ngIf="userform.controls.name.controls.lastName.errors.required">LastName is required</p>
</div>
</div>
</fieldset>
<div class="form-group" [ngClass]="{'has-danger': (userform.controls.email.invalid && (userform.controls.email.touched || userform.controls.email.dirty))}">
<label>Email</label>
<input type="email" class="form-control" formControlName="email"/>
<div class="form-control-feedback" *ngIf="userform.controls.email.errors && (userform.controls.email.dirty || userform.controls.email.touched)">
<p class="text-danger" *ngIf="userform.controls.email.errors.required">Email is required</p>
<p class="text-danger" *ngIf="userform.controls.email.errors.pattern">Not a valid pattern </p>
</div>
</div>
<div class="form-group" [ngClass]="{
'has-danger': userform.controls.password.invalid && (userform.controls.password.dirty || userform.controls.password.touched)}">
<label>Password</label>
<input type="password" class="form-control" formControlName="password" >
<div class="form-control-feedback" *ngIf="userform.controls.password.errors && (userform.controls.password.dirty || userform.controls.password.touched)">
<p class="text-danger" *ngIf="userform.controls.password.errors.required">Password is required</p>
<p class="text-danger" *ngIf="userform.controls.password.errors.minlength">Must be 8 characters long </p>
</div>
</div>
</form>
</div>
When application runs, model form errors can be seen when user try to edit(dirty), touch any field. Validation messages are displayed according to validators in input control of form model.
![](http://findnerd.s3.amazonaws.com/imagedata/9315/9315.png)
From above template it can be seen that validation expression like
userform.controls.name.controls.firstName.errors.required
are becoming too large which is difficult to handle. In order to write shorter validation expression component file needs to be changed to following.
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl,Validators } from '@angular/forms';
@Component({
selector: 'app-model-form',
templateUrl: './model-form.component.html'
})
export class ModelFormComponent implements OnInit {
userform: FormGroup;
firstName: FormControl;
lastName: FormControl;
email: FormControl;
password: FormControl;
ngOnInit() {
this.formControlsCreation();
this.formCreation();
}
formControlsCreation() {
this.firstName = new FormControl('', Validators.required);
this.lastName = new FormControl('', Validators.required);
this.email = new FormControl('', [Validators.required,Validators.pattern("[^ @]*@[^ @]*")]);
this.password = new FormControl('', [Validators.required,Validators.minLength(8)]);
}
formCreation() {
this.userform = new FormGroup({
name: new FormGroup({
firstName: this.firstName,
lastName: this.lastName,
}),
email: this.email,
password: this.password,
});
}
}
In above component local properties are created in component as a reflection of FormControls and thus directly binding these properties in component. These properties can be binded directly to component template without the need of going through top level instance of "userform" model. Now a template control for e.g: firstName can be rewritten as
<div class="form-group" [ngClass]="{'has-danger': (firstName.invalid && firstName.touched)}">
<label>First Name</label>
<input type="text" class="form-control" formControlName="firstName" />
<div class="form-control-feedback" *ngIf="firstName.errors && firstName.touched">
<p class="text-danger" *ngIf="firstName.errors.required">FirstName is required</p>
</div>
</div>
Similarly other template controls can be changed thus shortning the validation expression
0 Comment(s)