Angular 2 Form Validation

David Den Toom
Jan 4, 2016 · 3 min read

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

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store