Le Guide Angular | Marmicode
  • Le Guide Angular par Marmicode
  • Pourquoi Angular ?
  • ECMAScript 6+
    • Un Peu d'Histoire
    • Propriétés du Langage
    • "Single-Threaded" donc Asynchrone
    • Event Loop
    • Classes
    • Hoisting is Dead: var vs. let vs. const
    • this & "binding"
    • Arrow Functions
    • Template Strings
    • Syntactic Sugar
      • Spread
      • Destructuring
      • Rest
      • Object Literal Property Value Shorthand
    • Named Parameters
    • Compatibilité
  • TypeScript
    • Pourquoi TypeScript ?
    • De l'ECMAScript au TypeScript
    • Visibilité des Propriétés
    • Typing des Propriétés
    • Types
    • Interfaces
    • Inference
    • Duck Typing
    • Duck Typing Patterns
      • Compatibilité de Librairies
      • Entity Constructor
    • Décorateurs
      • Décorateurs de Propriété
      • Décorateurs de Classe
      • Décorateurs de Méthode & Paramètres
    • Quelques Liens
  • Tools
    • Clavier mécanique
    • Git
    • Command Line
    • NodeJS
    • NPM
    • Yarn
      • Pourquoi Yarn ?
      • Définition et Installation des Dépendances
      • Scripts
      • Mise à Jour et Automatisation
    • Chrome
    • IntelliJ / WebStorm / VSCode
      • Raccourcis clavier IntelliJ / WebStorm
    • Floobits
    • Angular CLI
    • StackBlitz
    • Compodoc
  • Angular
    • Bootstrap
    • Composants
      • Root Component
      • Template Interpolation
      • Property Binding
      • Class & Style Binding
      • Event Binding
      • *ngIf
      • *ngFor
      • L'approche MVC
      • Création de Composants
      • Exemple
    • Container vs. Presentational Components
    • Interaction entre Composants
      • Input
      • Output
      • Exemple
    • Change Detection
      • Les Approches Possibles
      • Fonctionnement de la Change Detection
      • Optimisation de la Change Detection
      • Immutabilité
      • Quelques Liens
    • Project Structure & Modules
      • Entry Point
      • Définition d'un Module
      • Root Module
      • Feature Module
      • Shared Module
      • Exemple
    • Dependency Injection
      • Qu'est-ce que la "Dependency Injection" ?
      • Injection d'un Service Angular
      • Services & Providers
      • Portée des Services
      • Tree-Shakable Services
      • Class vs Injection Token
      • Exemple
    • Callback Hell vs. Promise vs. Async / Await
      • Callback Hell
      • Promise
      • Async / Await
    • Observables
      • Reactive Programming
      • Promise vs Observable
      • Subscribe
      • Unsubscribe ⚠️
      • Création d'un Observable
      • Opérateurs
        • Définition d'un Opérateur
        • Lettable Operators vs Legacy Methods
        • map
        • filter
        • mergeMap & switchMap
        • shareReplay
        • buffer
        • debounceTime
        • distinctUntilChanged
        • retry
      • Quelques Liens
      • Talks
    • Http
      • Pourquoi HttpClient ?
      • Utilisation de HttpClient
      • Utilisation dans un Service
      • Gestion de la Subscription ⚠️
    • State Management
      • Quelques Liens
      • Talks
    • GraphQL
    • Formulaires
      • Template-driven Forms 🤢
      • Reactive Forms 👍
        • Avantages des "Reactive Forms"
        • La boite à outils des "Reactive Forms"
        • Validation
        • Observation des Changements
    • Directives
      • Attribute Directive
      • Structural Directive
    • Pipes
    • Routing
      • Mise en Place du Routing
      • Lazy Loading
      • Project Structure
      • Route Guards
    • Testing
      • Unit-Testing
        • 📺Introduction au Test-Driven Development
        • Jasmine
        • Unit-Test Synchrone
        • Test-Driven Development
        • Unit-Test Asynchrone
        • TestBed
        • Unit-Test d'un Service
        • Unit-Test d'un Composant
        • Unit-Test et Spies
        • Unit-Test et HttpClient
      • End-to-End
      • Talks
    • Sécurité
      • Quelques Liens
    • Animation
    • Internationalisation
    • Quelques Liens
  • Cookbook
    • Authentification et Autorisation
    • Remplacement Dynamique de Composants
    • Lazy Loading without Router
    • Project Structure
    • SCAM Modules
    • Setup a Mock ReSTful API
  • Autres Ressources
  • Stay Tuned
    • 🎁-20% sur nos workshops avec le code GUIDEANGULAR
    • 🐦Suivez-moi !
    • 📺Cours Vidéo
    • 📬Newsletter
    • 📝Blog
  • Nos Services
    • Formation Angular
    • Atelier Unit-Testing Angular
    • Atelier Architecture Angular
    • Consultation à Distance & Code Review
  • Nos Guides
    • Guide Agile
    • Guide API ReST
    • Guide NodeJS
Propulsé par GitBook
Sur cette page
  • "Validators"
  • Exemple de désactivation du "submit"
  • hasError & getError
  • "Validator" personnalisé
  • "Validator" paramétré
  • "Async Validators"'
  • Validators & "Dependency Injection"
  • Annulation des Observables
  • Personnalisation de l'affichage
  1. Angular
  2. Formulaires
  3. Reactive Forms 👍

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'un FormGroup ou FormArray, le "control" est valide si les "controls" qui le composent sont tous valides.

  • errors : "plain object" combinant les erreurs de tous les validateurs. Vaut null si le "control" est valide.

  • touched : Valeur booléenne positionnée à true dès le déclenchement de l'événement blur (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

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()
});

Les informations d'erreur (reason et value) sont alors accessibles grâce à la méthode getError.

const error = bookForm.getError('validBookTitle', 'title');
if (error != null) {
    console.log(error.reason);
    console.log(error.value);
}

"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;

    };

};
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()
});

Même l'erreur contient alors des informations personnalisées :

const error = bookForm.getError('validPattern', 'title');
if (error != null) {
    console.log(error.reason); // blacklisted
    console.log(error.value); // Become an Angular Ninja
    console.log(error.match); // Ninja
}

"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"

@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;
}
PrécédentLa boite à outils des "Reactive Forms"SuivantObservation des Changements

Dernière mise à jour il y a 5 ans

Ou en utilisant le "currying" des :

Les "validators" (et plus particulièrement les "validators" asynchrones) ont parfois besoin d'accéder aux services (au sens Angular) mais sans pour les "validators", l'astuce consiste à implémenter la "factory" du "validator" dans un service dédié afin de profiter de la "Dependency Injection".

Arrow Functions
Dependency Injection