Since Angular RC.2, Angular has introduced new forms modules and deprecated the existing one. With the new forms module, we can build forms with even more intuitive syntax.

There are two ways to build forms in Angular 2, namely model-driven and template-driven.

In this article, we will learn about buildling template-driven form with validation using the latest forms module, then we will talk about how the differences between the new template form and the deprecated one. Please refer to How to Build Model-driven Forms in Angular 2 if you would like to learn about model-driven forms.

Live demo on template-driven form with new forms module.

Live demo on template-driven form with deprecated forms module: http://plnkr.co/edit/tvALN2?p=preview

Introduction

We will build a form to capture user information based on this interface.

// user.interface.ts

export interface User {

name: string; // required with minimum 5 characters

address: {

street?: string; // required

postcode?: string;

}

}

Here is how the UI will look:

Requirements

Set postcode default value to 8000; Show error message only when:-

the field is invalid and it’s dirty (the field is touched/edited), or

and it’s (the field is touched/edited), or the field is invalid and the form is submitted

App Setup

As of RC.2 — RC.4, deprecated forms is enabled by default.

Here’s our file structure:

|- app/

|- app.component.html

|- app.component.ts

|- main.ts

|- user.interface.ts

|- index.html

|- styles.css

|- tsconfig.json

In order to use new forms module, we need to npm install @angular/forms npm package and enable it during application bootstrap.

$ npm install @angular/forms --save

Here’s the bootstrapping for our application in main.ts:

// main.ts

import { bootstrap } from '@angular/platform-browser-dynamic';

import { AppComponent } from './app/';

import { disableDeprecatedForms, provideForms } from '@angular/forms'; bootstrap(AppComponent, [

disableDeprecatedForms(), // disable deprecated forms

provideForms(), // enable new forms module

]);

The App Component

Let’s move on to create our app component.

// app.component.ts

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

import { User } from './user.interface'; @Component({

moduleId: module.id,

selector: 'my-app',

templateUrl: 'app.component.html',

})

export class AppComponent implements OnInit {

public user: User; // our model ngOnInit() {

// we will initialize our model here

} save(model: User, isValid: boolean) {

// check if model is valid

// if valid, call API to save customer

console.log(model, isValid);

}

}

The code is pretty simple and descriptive by itself.

The HTML View

This is how our HTML view will look like.

<div>

<h1>Add user</h1>

<form #f="ngForm" (ngSubmit)="save(f.value, f.valid)" novalidate>

<!-- we will place our fields here -->

<button type="submit">Submit</button>

</form>

</div>

Notes

The #f is the reference variable to the form directive. Refer to Angular official documentation for more details.

Layman explanation:

We need a way to retrieve the form data(all the values of our form fields like name, address, postcode, etc). Angular provide us a way, form is exported as ngForm. We then assign the form data to our local variable f.

save() function will be called when we submit the form.

Layman explanation:

f.value — an object that refer to the all the form field values.

f.valid — a Boolean that indicates whether the form is valid (e.g. name field is mandatory. If user name field is not filled, f.valid should be false).

Implementation

Now, let’s initialize our user model.

// app.component.ts

/* ... */

ngOnInit() {

// we will initialize our form here

this.user = {

name: '',

address: {

street: '',

postcode: '8000' // set default value to 8000

}

};

}

/* ... */

Let’s proceed to bind our user model to the view.

<!-- app.component.html -->

...

<form #f="ngForm" novalidate (ngSubmit)="save(f.value, f.valid)">

<!-- we will place our fields here --> <!--name-->

<div>

<label>Name</label>

<!--bind name to ngModel, it's required with minimum 5 characters-->

<input type="text"

name="name" [(ngModel)]="user.name"

#name="ngModel" required minlength="5">

<!--show error only when field is not valid & it's dirty or form submited-->

<small [hidden]="name.valid || (name.pristine && !f.submitted)">

Name is required (minimum 5 characters).

</small>

</div> <!--adrress group-->

<div ngModelGroup="address">

<!--street-->

<div>

<label>Street</label>

<input type="text"

name="street" [(ngModel)]="user.address.street"

#street="ngModel" required>

<small [hidden]="street.valid || (street.pristine && !f.submitted)" class="text-danger">

Street is required.

</small>

</div> <!--postcode-->

<div>

<label>Post code</label>

<input type="text"

name="postcode" [(ngModel)]="user.address.postcode">

</div>

</div> <button type="submit">Submit</button>

</form> ...

Notes

Use ngModel to bind the model to form control. The binding can be either one way or two way. In our case, we enable two way binding with banana in a box syntax [()]. Use name attribute to register a form control with the form. E.g. we assign “name” to the name attribute, then when we retrieve the name field from form value (f.value). Use ngModelGroup to group element. E.g. address is a group. Under address, there are street and postcode. #street is the reference variable to the input directive.

Similar to #f, we need a way to read the input data. Angular provide us a way, so input is exported as ngModel.

We then assign the input data to our local variable street.

With this, we can then use street.pristine, street.valid, etc to check if the street input is dirty, and its validity.

We use these values to show/hide the error message.

What are the differences with deprecated template forms?

There are a few improvements in in new form compared to the deprecated:-

Remove ngControl: since ngControl and ngModel behavior are quite similar (ngControl has extra validity checking features), it’s possible to combine ngControl into ngModel, additional nameattribute is required in the new forms module, those that are coming from Angular 1 shouldbe familiar with this. Rename NgControlGroup to NgModelGroup: since no more ngControl, so there will be no moreNgControlGroup but NgModelGroup. Exports renamed to ngModel: In deprecated form, control is exported as ngForm, it’s confusing as . E.g. previously, for the name field, the syntax is #name = “ngForm”, now it’s #name = “ngModel”. Added submitted flag to NgForm: with this improvment, we don’t need to define submitted property in our component.

For reference purpose, I’ve provided a live demo of the deprecated forms as well so you can see the differences.

Summary

Template forms is getting better with the new forms module. If you are curious about what are the new form changes, you may refer to Angular form changes proposal for more details.

How about if user is requires to add password and confirm password? How could we handle custom validation in template-driven forms? You might be interested in How to Implement Custom Validator Directive (Confirm Password) in Angular 2 for this use cases.

That’s it. Happy coding!

Live demo here on template-driven form with deprecated forms module: http://plnkr.co/edit/tvALN2?p=preview

Suggest for you:

Angular 2 and NodeJS — The Practical Guide to MEAN Stack 2.0

Learn Angular 2 Development By Building 10 Apps

Angular 2 — The Complete Guide (Updated to RC4!)

Angular 2 with TypeScript for Beginners: The Pragmatic Guide