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.validWe can also check the validity of just that individual control as well:
this.myForm.controls.title.validWe 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 alengthgreater than or equal to the specified minimummaxLength- the string or array must have alengthless 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 ascomposebut 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).