In this post a couple of weeks ago I covered Aurelia’s new validation plugin and I thought it would be good to see what Angular 2 has to offer validation wise. Angular’s offering is vastly different that Aurelia. Angular can use model driven or template driven validations. This post is going to look at the model driven style.
Forms
Angular forms is the framework’s way to handle groups of data entry that need support for data binding and validation among other things.
Building a Form with Validation
The first step to building a model driven form is import the classes that will be needed.
import {FormBuilder, ControlGroup, Validators} from '@angular/common';
Next add a variable for control group and setup the control group in the constructor of the class.
public contactForm: ControlGroup; constructor(formBuilder: FormBuilder) { this.contactForm = formBuilder.group({ name: ["", Validators.compose([Validators.required, Validators.minLength(3)])], email: ["", Validators.required] }); }
The above uses FormBuilder to define a name property that is required with a minimum length of three characters and an email property that is required. Later in the post I will add better email validation via customer validator.
Angular 2 only comes with required, minimum length, max length and pattern (match given regex) validators out of the box. Thankfully custom validators are pretty easy to implement.
Using a From with a View
Now that the form is setup the view needs to utilize it. The following is the code form the view.
<form class="form-horizontal" [ngFormModel]="contactForm"> <div class="form-group"> <label class="control-label">Name</label> <input type="text" ngControl="name" #name="ngForm" class="form-control"> <div [hidden]="name.valid || name.pristine || !name.errors.required">Required</div> <div [hidden]="name.valid || name.pristine || !name.errors.minlength">Min Length</div> </div> <div class="form-group"> <label class="control-label">Email</label> <input type="email" ngControl="email" #email="ngForm" class="form-control"> <div [hidden]="email.valid || email.pristine || !email.errors.required">Required</div> </div> </form>
First notice in the form tag [ngFormModel]=”contactForm” is used to bind the form to the control group crated in the model.
Instead of [(ngModel)]=”name” we use ngControl=”name” to bind the input box to the name control in the control group that was crated using the form builder above. #name=”ngForm” defines name as a variable that can be used later.
<div [hidden]=”name.valid || name.pristine || !name.errors.required”>Required</div> is used to display validation errors to the user. This code is just saying to hide the div if name is valid or pristine (not changed by the user) or if the validation error is not because the field is required. While this style works I much prefer Aurelia as it handles the extra work needed to automatically display validation errors.
Custom Validators
As I mention above I am going to show you a custom validator that can be used to better handle email validation. Most of this validation I found in a search but unfortunately I forgot to save the URL so I can’t link to the exact source. The following is the complete code for a custom email validator.
import {Control} from '@angular/common'; export class EmailValidator { static email(control: Control) { const emailRegexp = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; if (control.value !== "" && (control.value.length <= 5 || !emailRegexp.test(control.value))) { return { "email": true }; } return null; } }
Note that returning null means that validation passed.
Using a Custom Validator in a Model
First the custom validator needs to be imported.
import {EmailValidator} from './email.validator';
Now the validator can be used in the classes form builder.
this.contactForm = formBuilder.group({ name: ["", Validators.compose([Validators.required, Validators.minLength(3)])], email: ["", Validators.compose([Validators.required, EmailValidator.email])] });
Notices that the email control is now using Validators.compose which is used to apply multiple validations to a single control.
Using a Custom Validator in a View
Now the view just has to be changed to show the user that the reason for the failed validation is an invalid email address.
<div class="form-group"> <label class="control-label">Email</label> <input type="email" ngControl="email" #email="ngForm" class="form-control"> <div [hidden]="email.valid || email.pristine || !email.errors.required">Required</div> <div [hidden]="email.valid || email.pristine || !email.errors.email">Invalid address</div> </div>
Note that the entry in errors will match the return value from the custom validator. I had issues trying to use the minimum length validator because I was trying to use minLength instead of minlength. If you are having trouble with validation message showing up that would be the first thing to check.
Wrapping Up
That covers the basics of model driven validation in Angular 2. Be on the look out for a post in the future that covers the same concepts but using a template driven approach. I also wanted to point out this post by Pascal Precht and this post by David Den Toom which helped me a lot in writing this post.