Validation is an integral part of managing any set of forms. It improves overall data quality by validating user input for accuracy and completeness. Whether you’re checking for required fields or querying an external API for an existing username, Angular provides a set of built-in validators as well as the ability to create custom validators.
- Reactive forms define custom validators as functions that receive a control to validate.
- Template-driven forms are tied to template directives, and must provide custom validator directives that wrap validation functions.
Template-driven validation
To add validation to a template-driven form, you add the same validation attributes as you would with native HTML form validation. Angular uses directives to match these attributes with validator functions in the framework.
Every time the value of a form control changes, Angular runs validation and generates either a list of validation errors, which results in an INVALID status, or null, which results in a VALID status.
You can then inspect the control’s state by exporting ngModel to a local template variable. The following example exports NgModel into a variable called name:
template/hero-form-template.component.html (name)
<input id=”name” name=”name” class=”form-control”
required minlength=”4″ forbiddenName=”bob”
[(ngModel)]=”hero.name” #name=”ngModel” ><div *ngIf=”name.invalid && (name.dirty || name.touched)”
class=”alert alert-danger”>
<div *ngIf=”name.errors.required”>
Name is required.
</div>
<div *ngIf=”name.errors.minlength”>
Name must be at least 4 characters long.
</div>
<div *ngIf=”name.errors.forbiddenName”>
Name cannot be Bob.
</div>
</div>
Note the following:
- The <input> element carries the HTML validation attributes: required and minlength. It also carries a custom validator directive, forbiddenName.
- #name=”ngModel” exports NgModel into a local variable called name. NgModel mirrors many of the properties of its underlying FormControl instance, so you can use this in the template to check for control states such as valid and dirty.
- The *ngIf on the <div> element reveals a set of nested message divs but only if the name is invalid and the control is either dirty or touched.
- Each nested <div> can present a custom message for one of the possible validation errors. There are messages for required, minlength, and forbiddenName.
Why check dirty and touched? – You may not want your application to display errors before the user has a chance to edit the form. The checks for dirty and touched prevent errors from showing until the user does one of two things: changes the value, turning the control dirty; or blurs the form control element, setting the control to touched.
Reactive form validation
In a reactive form, the source of truth is the component class. Instead of adding validators through attributes in the template, you add validator functions directly to the form control model in the component class. Angular then calls these functions whenever the value of the control changes.
Validator functions – There are two types of validator functions: sync validators and async validators.
- Sync validators: functions that take a control instance and immediately return either a set of validation errors or null. You can pass these in as the second argument when you instantiate a FormControl.
- Async validators: functions that take a control instance and return a Promise or Observable that later emits a set of validation errors or null. You can pass these in as the third argument when you instantiate a FormControl.
For performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
Built-in validators – You can choose to write your own validator functions, or you can use some of Angular’s built-in validators.
The same built-in validators that are available as attributes in template-driven forms, such as required and minlength, are all available to use as functions from the Validators class. To update the hero form to be a reactive form, you can use some of the same built-in validators—this time, in function form. See below:
reactive/hero-form-reactive.component.ts (validator functions)
ngOnInit(): void {
this.heroForm = new FormGroup({
‘name’: new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <– Here’s how you pass in the custom validator.
]),
‘alterEgo’: new FormControl(this.hero.alterEgo),
‘power’: new FormControl(this.hero.power, Validators.required)
});
}
get name() { return this.heroForm.get(‘name’); }
get power() { return this.heroForm.get(‘power’); }
Note that:
- The name control sets up two built-in validators—Validators.required and Validators.minLength(4)—and one custom validator, forbiddenNameValidator.
- As these validators are all sync validators, you pass them in as the second argument.
- Support multiple validators by passing the functions in as an array.
- This example adds a few getter methods. In a reactive form, you can always access any form control through the get method on its parent group, but sometimes it’s useful to define getters as shorthands for the template.