Validation
"Validators"
Les constructeurs des "controls" (FormControl
, FormGroup
et FormArray
) acceptent en second paramètre une liste de fonctions de validation appelées "validators".
Les "validators" natifs d'Angular sont regroupés sous forme de méthodes statiques das la classe Validators
.
export class BookFormComponent {
bookForm = new FormGroup({
title: new FormControl(null, [
Validators.required
]),
description: new FormControl()
});
submitBook() {
console.log(this.bookForm.value);
}
}
Remarquez que l'ajout du "validator" ne change pas le comportement du composant : la méthode submitBook
continue à être appelée bien que la contrainte de validation ne soit pas respectée.
C'est au composant de décider de l'action à mener en fonction de l'état des "controls".
Les "controls" disposent d'une série de propriétés et de méthodes permettant d'en vérifier l'état :
valid
: Valeur booléenne indiquant si le "control" est valide. Dans le cas d'unFormGroup
ouFormArray
, le "control" est valide si les "controls" qui le composent sont tous valides.errors
: "plain object" combinant les erreurs de tous les validateurs. Vautnull
si le "control" est valide.touched
: Valeur booléenne positionnée àtrue
dès le déclenchement de l'événementblur
(i.e. l'utilisateur change de "focus").pristine
: Valeur booléenne indiquant si le "control" a été modifié.
Exemple de désactivation du "submit"
<button
[disabled]="!bookForm.valid"
type="submit">SUBMIT</button>
hasError
& getError
hasError
& getError
Les méthodes hasError
et getError
sont deux méthodes "helpers" permettant d'accéder plus facilement aux informations d'erreur d'un "control".
<div *ngIf="shouldShowTitleRequiredError()">Title is required.</div>
shouldShowTitleRequiredError() {
const title = this.bookForm.controls.title;
return title.touched && title.hasError('required');
}
Préférez les méthodes hasError
et getError
aux opérateurs ternaires (title.errors ? title.errors.required : null
) !
"Validator" personnalisé
Un "validator" est une fonction qui est appelée à chaque changement de la valeur du "control" afin d'en vérifier la validité. Si la valeur est valide, le "control" retourne null
ou un objet d'erreur dans le cas contraire.
import { ValidatorFn } from '@angular/forms';
export const validBookTitle: ValidatorFn = (control) => {
/* Is not valid. */
if (/learn .*? in one day/i.test(control.value)) {
return {
'validBookTitle': {
reason: 'blacklisted',
value: control.value
}
};
}
/* Is valid. */
return null;
};
bookForm = new FormGroup({
title: new FormControl(null, [
Validators.required,
validBookTitle
]),
description: new FormControl()
});
"Validator" paramétré
Tels que le "validator" minLength
, certains "validators" ont besoin de paramètres pour personnaliser leur comportement.
Dans ce cas, il suffit d'implémenter une "factory" de "validators" (i.e. : une fonction qui retourne des fonctions de type "validator").
export type ValidPattern = (args: {blacklistedPatternList: RegExp[]}) => ValidatorFn;
const validPattern: ValidPattern = ({blacklistedPatternList}) => {
return (control) => {
for (const blacklistedPattern of blacklistedPatternList) { ... }
return null;
};
};
Ou en utilisant le "currying" des Arrow Functions :
export type ValidPattern = (args: {blacklistedPatternList: RegExp[]}) => ValidatorFn;
const validPattern: ValidPattern = ({blacklistedPatternList}) => control => {
for (const blacklistedPattern of blacklistedPatternList) {
const result = blacklistedPattern.exec(control.value);
if (result != null) {
return {
'validPattern': {
reason: 'blacklisted',
match: result[0],
value: control.value
}
};
}
}
return null;
};
Le "validator" est alors personnalisé à l'utilisation :
bookForm = new FormGroup({
title: new FormControl(null, [
Validators.required,
validPattern({
blacklistedPatternList: [
/^learn .*? in one day/i,
/hero/i,
/ninja/i
]
})
]),
description: new FormControl()
});
"Async Validators"'
Il est parfois nécessaire d'implémenter des "validators" asynchrones (e.g. vérification distante via une API).
Un "validator" asynchrone se comporte de la même façon qu'un "validator" synchrone mais au lieu de retourner null
ou un objet d'erreur, il doit retourner un Observable
.
Les "validators" asynchrones peuvent être transmis au "control" par paramètre ordonné (3ème paramètre après la valeur initiale et les validators synchrones) mais il est préférable d'utiliser un objet plus explicite en guise de second paramètre :
new FormControl(null, {
validators: [Validators.required],
asyncValidators: [myAsyncValidator]
})
Validators & "Dependency Injection"
Les "validators" (et plus particulièrement les "validators" asynchrones) ont parfois besoin d'accéder aux services (au sens Angular) mais sans Dependency Injection pour les "validators", l'astuce consiste à implémenter la "factory" du "validator" dans un service dédié afin de profiter de la "Dependency Injection".
@Injectable({
providedIn: 'root'
})
export class ValidPatternFactory {
constructor(private _httpClient: HttpClient) {
}
create(): AsyncValidatorFn {
return control => {
return this._httpClient.post<ApiValidatorResult>('/validations', {
validatorId: 'VALIDATOR_ID',
keywords: control.value
})
.pipe(map(result => result.errors));
};
}
}
export class BookFormComponent {
bookForm: FormGroup;
constructor(private _validPatternFactory: ValidPatternFactory) {
this.bookForm = new FormGroup({
title: new FormControl(null, {
validators: [Validators.required],
asyncValidators: [this._validPatternFactory.create()]
}),
description: new FormControl()
});
}
}
Annulation des Observables
Dès la saisie d'une nouvelle valeur, l'Observable
récupéré précédemment sera annulé avant de déclencher le traitement du nouvel Observable
retourné (via un subscribe
).
Personnalisation de l'affichage
Afin de faciliter la personnalisation du CSS en fonction de la validité des éléments du formulaire, Angular ajoute automatiquement les classes CSS suivantes en fonction de l'état du "control" :
.ng-valid
.ng-invalid
.ng-pending
.ng-pristine
.ng-dirty
.ng-untouched
.ng-touched
input.ng-touched.ng-invalid {
border-width: 1px;
border-left: red solid 5px;
}
Dernière mise à jour