Angular 2 has reached beta, it’s time to explore the framework. In my opinion one of the best new features is the way Angular 2 handles forms. It doesn't seem to be getting a lot of attention and in this post I want to explain you how to implement the following:

Built-in form validation

Custom form validation

Asynchronous form validation

Combining multiple validators

Let’s start with the basics

Angular 2 forms work with two main components: Controls and Control Groups

Controls

“ Controls have values and validation state, which is determined by an optional validation function.”

A Control can be bound to an input element, and takes 3 arguments (all optional); a default value, a validator and a asynchronous validator.

For example

this.username = new Control('Default value', Validators.required, UsernameValidator.checkIfAvailable);

Which can be used in your HTML using the “ngControl” directive

<input required type=”text” ngControl=”username” />

Control Groups

Defines a part of a form, of fixed length, that can contain other controls.

Multiple Controls can be used to create a Control Group. We use Angular’s “FormBuilder” Class to combine multiple Controls.

class App {



name: Control;

username: Control;

email: Control;



form: ControlGroup;



constructor(private builder: FormBuilder) {



this.name = new Control('', Validators.required);

this.email = new Control('', Validators.required);

this.username = new Control('', Validators.required);



this.form = builder.group({

name: this.name,

email: this.email,

username: this.username

});

} };

The ControlGroup can be used in your HTML, bound to your form, using the ngFormModel directive.

<form [ngFormModel]=”form”>

Built-in form validation

Angular 2 provides three out of the box validators which can either be applied using the “Control” Class, or using HTML properties.

(For example: <input required> will automatically apply the required validator)

Required

minLength

maxLength

Apply them to the Control using the second parameter.

this.name = new Control('', Validators.minLength(4));

And use the following HTML to show the related error message

<input required type=”text” ngControl=”name” /> <div *ngIf=”name.dirty && !name.valid”>

<p *ngIf=”name.errors.minlength”>

Your name needs to be at least 4 characters.

</p>

</div>

Custom form validation

Of course we can also write our own custom validators. Here is an example of a validator that checks if the first character is not a number:

interface ValidationResult {

[key:string]:boolean;

} class UsernameValidator {



static startsWithNumber(control: Control): ValidationResult {



if ( control.value !=”” && !isNaN(control.value.charAt(0)) ){

return { “startsWithNumber”: true };

}



return null; }



}

One weird thing you might notice is that returning null actually means the validation is valid. If we find a number at the first position of the string we return the validation error { “startsWithNumber”: true }

Now we can use this Validator in our Control

this.name = new Control('', UsernameValidator.startsWithNumber);

And in our HTML

<input required type=”text” ngControl=”name” /> <div *ngIf=”name.dirty && !name.valid”>

<p *ngIf=”name.errors.startsWithNumber”>

Your name can't start with a number

</p>

</div>

Asynchronous form validation

If we want to check something using a Promise (such as fetching data from the server) we can use an asynchronous validator. It works quite similar to the default Validator, only this time we want to return a promise.

class UsernameValidator {



…



static usernameTaken(control: Control): Promise<ValidationResult> {



let q = new Promise((resolve, reject) => {

setTimeout(() => {

if (control.value === ‘David’) {

resolve({“usernameTaken”: true});

} else {

resolve(null);

}

}, 1000)

});



return q;

}



}

This time we want to use the third parameter of the Control to apply the validator.

this.name = new Control('', UsernameValidator.startsWithNumber, UsernameValidator.usernameTaken);

In our HTML we wan’t to show 2 different states, when the Promise is pending and the validation message if needed.

<input required type=”text” ngControl=”name” /> <p *ngIf=”name.pending”>

Fetching data from the server...

</p> <div *ngIf=”name.dirty && !name.valid && !name.pending”>

<p *ngIf=”name.errors.startsWithNumber”>

Your name can't start with a number

</p> <p *ngIf=”name.errors.usernameTaken”>

This username is already taken

</p>

</div>

Combining multiple validators

This last part is very easy, Angular 2 provides two methods to combine Validators. Validators.compose and Validators.composeAsync. Both take an Array of validators.

this.name = new Control('', Validators.compose([Validators.required, Validators.minLength(4)]));

Example code

You can find the working example here

https://github.com/daviddt/angular2-form-validation-example