Angular 2 Forms
We will go through the following topics:
- What is Angular 2 Forms all about
- Template Driven Forms, or the Angular 1 way
- Model Driven Forms, or the new Functional Reactive way
- Updating Form Values
- How To Reset a Form
- Advantages and disadvantages of both form types
- Can and should the two be used together ?
- Which one to choose by default ?
Angular 2 Forms - what is it all about?
Many of frontend applications applications are basically just huge forms, spanning multiple tabs and dialogs and with non-trivial validation business logic.
Every form-intensive application has to provide answers for the following problems:
- how to keep track of the global form state
- know which parts of the form are valid and which are still invalid
- properly displaying error messages to the user so that the user knows what to do to make the form valid
Template driven
- Elements like ngModel, ngModelGroup, ngForm
- implicitly created
- asynchronous, lots of stuff going on behind the scenes for wiring up the FormControl instance etc.
Reactive, or model-driven
- Elements like formControlName, formGroupName, formArrayName, formControl, formGroup
- explicitly/programmatically created
- synchronous and more predictive as they’re created programmatically and there’s not template rendering in the middle
Two different approaches
Enabling Template Driven Forms
import {NgModule} from "@angular/core";
import {platformBrowserDynamic} from "@angular/platform-browser-dynamic";
import {BrowserModule} from "@angular/platform-browser";
import {FormsModule} from "@angular/forms";
@Component({...})
export class App { }
@NgModule({
declarations: [App],
imports: [BrowserModule, FormsModule],
bootstrap: [App]
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule);
Runnable Plunker
Our First Template Driven Form
<section class="sample-app-content">
<h1>Template-driven Form Example:</h1>
<form #f="ngForm" (ngSubmit)="onSubmitTemplateBased()">
<p>
<label>First Name:</label>
<input type="text"
[(ngModel)]="user.firstName" required>
</p>
<p>
<label>Password:</label>
<input type="password"
[(ngModel)]="user.password" required>
</p>
<p>
<button type="submit" [disabled]="!f.valid">Submit</button>
</p>
</form>
</section>
NgModel Validation Functionality
Angular is actually tracking three form field states for us and applying the following CSS classes to both the form and its controls:
- touched or untouched
- valid or invalid
- pristine or dirty
These CSS state classes are very useful for styling form error states.
Controller
@Component({
selector: "template-driven-form",
templateUrl: 'template-driven-form.html'
})
export class TemplateDrivenForm {
user: Object = {};
onSubmitTemplateBased() {
console.log(this.vm);
}
}
Not much to see here! We only have a declaration for a view model object user, and an event handler used by ngSubmit.
How does Angular pull this off then?
The way that this works, is that there is a set of implicitly defined form directives that are being applied to the view. Angular will automatically apply a form-level directive to the form in a transparent way, creating a FormGroup and linking it to the form.
ngNoForm
required | maxlength
The presence of [(ngModel)] will also create a bidirectional binding between the form and the user model, so in the end there is not much more to do at the level of the controller.
Field initialization
<p>
<label>First Name:</label>
<input type="text" [ngModel]="user.firstName" required>
</p>
<p>
<label>Password:</label>
<input type="password" [ngModel]="user.password" required>
</p>
user = {
firstName: 'John',
password: 'test'
};
Validation
<p>
<label>First Name:</label>
<input type="text" name="firstName" required ngModel>
</p>
<p>
<label>Password:</label>
<input type="password" name="password" required ngModel>
</p>
Advantages and Disadvantages of Template Driven Forms
Disadvantages:
- pretty hard to read rather quickly
- complex cross-field validations will decreases readability
- form validation logic cannot be unit tested
Advantages: simplicity
Functional programming point of view
Bi-directional binding is a solution that promotes mutability
Each form has a state that can be updated by many different interactions
Model Driven Or Reactive Forms
import {NgModule} from "@angular/core";
import {ReactiveFormsModule} from "@angular/forms";
@Component({...})
export class App { }
@NgModule({
declarations: [App],
imports: [BrowserModule, ReactiveFormsModule],
bootstrap: [App]
})
export class AppModule {}
First Reactive Form
<section class="sample-app-content">
<h1>Model-based Form Example:</h1>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<p>
<label>First Name:</label>
<input type="text" formControlName="firstName">
</p>
<p>
<label>Password:</label>
<input type="password" formControlName="password">
</p>
<p>
<button type="submit" [disabled]="!form.valid">Submit</button>
</p>
</form>
</section>
Controller
import { FormGroup, FormControl, Validators, FormBuilder }
from '@angular/forms';
@Component({
selector: "model-driven-form",
templateUrl: 'model-driven-form.html'
})
export class ModelDrivenForm {
form: FormGroup;
firstName = new FormControl("", Validators.required);
constructor(fb: FormBuilder) {
this.form = fb.group({
"firstName": this.firstName,
"password":["", Validators.required]
});
}
onSubmitModelBased() {
console.log("model-based form submitted");
console.log(this.form);
}
}
ngModel?
Plunker
FormGroup and FormControl code
Plunker
FormBuilder code
Advantages and disadvantages of Model Driven Forms
We can now unit test the form validation logic
The FormGroup and
FormControl classes provide an API that allows us to build UIs using a completely different programming style known as Functional Reactive Programming.
Functional Reactive Programming
this.form.valueChanges
.map((value) => {
value.firstName = value.firstName.toUpperCase();
return value;
})
.filter((value) => this.form.valid)
.subscribe((value) => {
console.log("Model Driven Form valid value: vm = ",JSON.stringify(value));
});
Advantages of Functional Reactive Programming (FRP)
- pre-save the form in the background at each valid state, or even invalid (for example storing the invalid value in a cookie for later use)
- typical desktop features like undo/redo
How To Reset a Form
reset() {
this.form.reset();
}
Model-Driven vs Template Driven: can they be mixed ?
If by some reason we would need to, we could mix and match the two ways of building forms, for example:
- we can use ngModelto read the data, and use FormBuilder for the validations. we don't have to subscribe to the form or use RxJs if we don't wish to.
- We can declare a control in the controller, and then reference it in the template to obtain its validity state
But in general its better to choose one of the two ways of doing forms, and using it consistently throughout the application.
Which form type to choose, and why ?
Are you migrating an Angular 1 application into Angular 2? That is the ideal scenario for using Template Driven Forms.
Or are you building a new application from scratch ? Reactive forms are a good default choice, because more complex validation logic is actually simpler to implement using them.
Summary
- Angular 2 provides with us ways to build forms: Template Driven and Model Driven.
- The Template Driven approach is very familiar to Angular 1 developers, and is ideal for easy migration of Angular 1 applications into Angular 2.
- The Model Driven approach removes validation logic from the template, keeping the templates clean of validation logic. But also allows for a whole different way of building UIs that we can optionally use.
- This is not an exclusive choice, but for a matter of consistency its better to choose one of the two approaches and use it everywhere in our application.
Related Links
- The Ultimate Guide to Forms in Angular 2
- If you want to know more about Angular 2 Forms, the podcast of Victor Savkin on Angular Air goes into detail on the two form types and
ngModel. -
This blog post gives a high level overview of how Angular 2 will better enable Functional Reactive Programming techniques.
-
If you are interested in learning about how to build components in Angular 2, check also The fundamentals of Angular 2 components.
Angular 2 Forms
By Pavel Nasovich
Angular 2 Forms
- 2,988