
Existing member? Log in and continue learning

See if you like it, start the course for free!

Unlock full course by purchasing a membership
Advanced Validations
Advanced Validations
We have already seen some basic validations, for example, in a previous application we have built a form like this:
myForm = this.fb.nonNullable.group({ title: ['', Validators.required],});
The general idea here is that if any of the Validators
fail against the input
for that control, then the valid
property of the form will be false
, e.g:
this.myForm.valid
We can also check the validity of just that individual control as well:
this.myForm.controls.title.valid
We can also use multiple validators for a single field by providing an array of validators:
myForm = this.fb.nonNullable.group({ title: ['', [Validators.required, Validators.minLength(5)]],});
By default, Angular comes with a range of validators that suit most cases:
min
— value must be greater than or equal to the specified minimummax
— value must be less than or equal to the specified maximumrequired
— value must be a non-empty valuerequiredTrue
— value must be trueemail
— string must match an email pattern testminLength
— the string or array must have alength
greater than or equal to the specified minimummaxLength
- the string or array must have alength
less than or equal to the specified maximumpattern
— the value must match the specified regex patternnullValidator
— a validator that does nothing, can be useful if you need to return a validator but don’t want it to do anythingcompose
— allows you to bundle multiple validators together into one validatorcomposeAsync
— same ascompose
but allows for asynchronous validations
But, sometimes this is not enough and we might require a validator that is custom made for our specific situation. We are going to cover a couple of advanced techniques for validation in this lesson. We will cover how to create our own asynchronous validator, and also our own custom validator that performs a validation across multiple fields within the form.
Custom Validators
Creating our own custom validators is actually reasonably easy — the general
idea is we create a function that accepts a control
and we return null
if
the validation passes or we return a map of validation errors if it fails.
As an example, we might create a custom validator at
src/app/shared/utils/adult-validator.ts
that looks like this:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export const adultValidator: ValidatorFn = ( control: AbstractControl): ValidationErrors | null => { return control.value >= 18 ? null : { adultValidator: true };};
We have the control
passed in and we access its value
. We check if that
value
is greater than or equal to 18
. If it is, we return null
which means
the validation has passed. If it is not, we return an object indicating the
validation errors, which is:
{ adultValidator: true}
I think this can be kind of confusing, because providing this map of errors kind
of reads as if the adultValidator
passed because it is listed as true
. But
really, this is saying that there was an error with adultValidator
.
We can then import and use this custom validator in our form like any other validator, e.g:
myForm = this.fb.nonNullable.group({ title: ['', Validators.required], age: [null, adultValidator],});
This is a simple example, and really we could just use the min
validator for
this, but we will look at more advanced implementations in the next two
examples.
An Asynchronous Validator
A great example of an asynchronous validator is validating whether or not
a given username
has been taken or not. Most validators work synchronously
— we instantly know if they are valid or not. But for something like this, we
would need to make a request to a server with the value provided to check it,
and that is going to take some time.
The validator we would create for this situation is a little different. We could
create this validator somewhere like
src/app/shared/utils/username-available-validator.ts
but it might look more
like this:
import { Injector } from '@angular/core';import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';import { catchError, map, Observable, of } from 'rxjs';import { UserService } from '../data-access/user.service';
export const usernameAvailableValidator: ValidatorFn = ( control: AbstractControl): Observable<ValidationErrors | null> => { const injector = Injector.create([ { provide: UserService, useClass: UserService }, ]); return injector .get(UserService) .checkUsernameAvailable(control.value) .pipe( map((isAvailable) => (isAvailable ? null : { usernameAvailable: true })), catchError(() => of(null)) );};
Thanks for checking out the preview of this lesson!
You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).