Angular 2 Form Validation

DISCLAIMER 
This example is not up to date for Angular v4. I’m planning to update the example soon.


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