Formulaires

Rappels

On a vu jusqu’à présent différents manières de lier des données entre classe et template.

ngModel

  1. Importer FormsModule dans le module racine
    app.module.ts

     +import { FormsModule } from '@angular/forms';
    
     @NgModule({
       imports: [
         BrowserModule,
     +   FormsModule
       ]
    
  2. Utiliser la directive ngModel, entourée de parenthèses dans des crochets (appelée la syntaxe “banana in a box”).

     <input type="checkbox" [(ngModel)]="isChecked">
    

    ngModel prend en charge tout un tas d’élément: input texte, checkbox, radio, textarea, select, etc.

ngModelChange

ngForm

ngModelGroup

Valeur initiale


ngSubmit

Propriétés de validation


Reactive Forms

  1. Importer ReactiveFormsModule dans le module racine
    app.module.ts

     +import { FormsModule, ReactiveFormsModule } from '@angular/forms';
    
     @NgModule({
       imports: [
         BrowserModule,
         FormsModule,
     +   ReactiveFormsModule
       ]
    
  2. Créer une instance de FormGroup, qui servira à contenir les champs du formulaire, et le remplir avec des instances FormControl, qui s’occuperont de contrôler les champs.

     import { FormGroup, FormControl } from '@angular/forms';
    
     ...
     export class AppComponent {
       registrationForm = new FormGroup({
         username: new FormControl('Initial value'),
         email   : new FormControl(''),
    
         address: new FormGroup({
           street: new FormControl(''),
           postalCode: new FormControl(''),
           city: new FormControl('')
         })
       });
     }
    

    Ou, pour plus de lisibilité, on peut utiliser le service FormBuilder — qui crée les instances FormGroup et FormControl en utilisant les paramètres spécifiés.

     import { FormBuilder } from '@angular/forms';
    
     ...
     export class AppComponent {
       registrationForm = this.fb.group({
         username: ['Initial value'],
         email   : [''],
    
         address: this.fb.group({
           street: [''],
           postalCode: [''],
           city: ['']
         })
       });
    
       constructor(private fb: FormBuilder) {}
     }
    
  3. Dans le template, lier les FormGroup et FormControl aux éléments du formulaire avec formGroup, formGroupName et formControlNamed.

     <form [formGroup]="registrationForm">
       <pre>{{ registrationForm.value | json }}</pre>
       <!--
       {
         "username": "Initial value",
         "email": "",
         "address": {
           "street": "",
           "postalCode": "",
           "city": ""
         }
       }
       --->
    
       <div>
         <label for="name">Name</label>
         <input id="name" type="text" formControlName="username">
       </div>
    
       <div>
         <label for="email">Email</label>
         <input id="email" type="email" formControlName="email">
       </div>
    
       <div formGroupName="address">
         <div>
           <label>Street</label>
           <input type="text" formControlName="street">
         </div>
    
         <div>
           <label>Postal Code</label>
           <input type="text" formControlName="postalCode">
         </div>
    
         <div>
           <label>City</label>
           <input type="text" formControlName="city">
         </div>
       </div>
    
      </form>
    

Valeur initiale d’un FormControl

FormArray


Validateur

Validation dynamique

Validateur personnalisé

  1. Déclarar une fonction qui prend pour paramètre un objet AbstractControl — ce qui correspond à un FormControl ou à un FormGroup suivant l’objet sur lequel on ajoute la validateur (en l’occurence, ce sera un FormControl).

    La fonction doit retourner null si le champ est valide ou un objet si invalide, où la clé est le type d’erreur et la valeur est la valeur ayant échoué la validation.

     import { AbstractControl } from '@angular/forms';
    
     export function forbiddenValueValidator
         (control: AbstractControl): {[key: string]: any} | null {
    
       const forbidden = /admin/.test(control.value);
       return forbidden ? {'forbiddenValue': control.value} : null;
     }
    
  2. Spécifier cette fonction comme validateur.

     import { forbiddenValueValidator } from './shared/forbiddenValue.validator';
    
     ...
     username: ['', [
       Validators.required,
       Validators.minLength(3),
       forbiddenValueValidator
     ]]
    
  3. Dans le template, tester le type de l’erreur pour afficher un message d’erreur approprié

     <div *ngIf="$.username.errors?.forbiddenValue">
       <small>Username "{{ $.username.errors.forbiddenValue }}" is not allowed</small>
     </div>
    

Validateur personnalisé avec paramètre

Validateur avec plusieurs champs

  1. Créer un validateur personnalisé pour comparer les deux champs.
    L’objet AbstractControl correspondra ici à un FormGroup puisqu’on va ajouter le validateur sur un groupe.

     import { AbstractControl } from '@angular/forms';
    
     export function passwordValidator
       (control: AbstractControl): {[key: string]: any} | null {
    
         const password        = control.get('password');
         const confirmPassword = control.get('confirmPassword');
    
         if(password.pristine || confirmPassword.pristine) {
           return null;
         }
         return password
             && confirmPassword
             && password.value !== confirmPassword.value
              ? {'passwordMismatch': true} : null;
     }
    
  2. Spécifier cette fonction comme validateur du groupe.
    Le deuxième paramètre de FormGroup est un objet d’options, où validator spécifie un ou plusieurs validateurs sur le groupe.

     registrationForm = this.fb.group({
       username: ['Bob', Validators.required],
       password: [''],
       confirmPassword: ['']
     }, {
       validator: passwordValidator
     })
    
  3. Utiliser les erreurs sur le groupe pour afficher un message d’erreur si nécessaire.

     <input type="password" formControlName="confirmPassword">
    
     <div *ngIf="registrationForm.errors?.mismatchPassword">
       <small>Passwords do not match</small>
     </div>