Archives Wild Code School
Angular

15 - Les formulaires : Reactive Form et FormGroup

Quête 12 sur 16

Dans la quête précédente tu as vu comment contrôler un champ de formulaire sans utiliser ngModel. C'est intéressant mais pas très pratique pour gérer de gros formulaires. C'est la qu'interviennent les FormGroup. Ils vont te permettre de regrouper des FormControl. Tu les placeras directement sur le formulaire.

Tu repartiras de ce que tu as déjà fait dans la quête précédente. Pour le moment, tu as plus de champs que dans les exemples qui suivent. Lis une 1ère fois l'ensemble de la quête puis adapte ton code en fonction.

Objectifs

  • Regrouper des FormControl dans un FormGroup
  • Récupérer les valeurs entrées dans le formulaire lors du submit à partir du FormGroup
  • Utiliser FormBuilder

Regroupement en FormGroup

Dans la quête précédente tu avais créé un champ username contrôlé :

Un FormGroup est une classe qui prend en paramètre, lors de sa création, une structure contenant des FormControl.

Tu vas mettre ton champ username dans un même FormGroup que tu appellerasuserForm.

typescript
import { FormGroup, FormControl } from '@angular/forms';
...

@Component(...)
export class UserComponent {

    userForm = new FormGroup({
         username: new FormControl('')
    });
}

Utilisation de FormBuilder

La création d'un formulaire peut devenir répétitive quand il commence à devenir grand. Angular fourni un FormBuilder qui va te permettre de créer des formulaires de manière plus pratique. C'est un service que tu devras injecter dans ton constructeur.

typescript
import { FormBuilder } from '@angular/forms';
...

@Component(...)
export class UserComponent {

    userForm = this.fb.group({
        username: ['']
    });
    constructor(private fb: FormBuilder) {}

}

Il est conseillé d'utiliser cette syntaxe qui est plus compréhensive.

Liaison avec le template html

Un FormGroup peut se binder sur une balise <form> dans un template html en utilisant la directive [formGroup].

userForm est un attribut de ton composant, ce qui veut dire que tu peux l'utiliser dans le template html.

html
<form [formGroup]="userForm">
    ...
</form>

Maintenant que tu as bindé ton FormGroup tu dois définir quel FormControl est relié à quel champ html de ton formulaire. Pour cela, tu dois utiliser la directive formControlName="nomFormControl" que tu peux placer sur n'importe quel champ de formulaire :

shell
<form [formGroup]="userForm">
    <div>
        <label>
            Username:
            <input type="text" formControlName="username">
        </label>
    </div>
</form>

Submit

Pour l'instant, ton formulaire n'a pas de bouton submit pour soumettre le formulaire. Tu dois en ajouter un. Tu dois également définir une fonction à exécuter à l'aide de la directive (ngSubmit)="maFonction()" que tu places sur la balise <form>

html
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">

    ...
    <button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>

Ton attribut userForm de type FormGroup possède plusieurs propriétés qui te seront très utiles comme la propriété valid par exemple. Tu trouveras l'ensemble des propriétés utilisables sur la documentation.

A toi d'effectuer le traitement que tu souhaites lors du submit. Tout comme un FormControl, un FormGroup possède un attribut value qui va représenter les valeurs rentrées dans le formulaire qu'il contrôle.

typescript
...

@Component(...)
export class UserComponent {

    userForm = this.fb.group({
        username: ['']
    });

    onSubmit() {
        console.log(this.userForm.value);
    }
}

Regroupement de FormGroup

Parfois, tu vas vouloir diviser ton formulaire en plusieurs sous FormGroup. Cela tombe plutôt bien car tu peux imbriquer des FormGroup entre eux.

Tu vas ajouter des informations d'adresse dans le formulaire que tu viens de créer et tu vas les regrouper dans un FormGroup que tu imbriqueras dans ton FormGroup existant userForm

Sans FormBuilder :

typescript
...

// Sans FormBuilder

@Component(...)
export class UserComponent {

    userForm = new FormGroup({
         username: new FormControl(''),
         // Gestion de l'adresse avec un FormGroup imbriqué
         address: new FormGroup({
             street: new FormControl(''),
             city: new FormControl(''),
             zipCode: new FormControl('')
         })
    });

    ...
}

Avec FormBuilder :

typescript
...

// Avec FormBuilder

@Component(...)
export class UserComponent {

    userForm = this.fb.group({
        username: [''],
        address: this.fb.group({
            street: [''],
            city: [''],
            zipCode: ['']
        })
    });

    ...
}

Dans ton template html, tu peux maintenant lier ton FormGroup imbriqué appelé address à l'aide de la directive formGroupName. Tu peux la placer sur l'élément html que tu souhaites à condition qu'il soit parent des champs FormControl qu'il contient.

html
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    ...

    <!-- address est le nom du FormGroup imbriqué -->
    <div formGroupName="address">
        <div>
            <label>
                Street:
                <!--
                     "street" correspond à la propriété street du FormGroup address
                     contenu dans le FormGroup userForm
                -->
                <input type="text" formControlName="street">
            </label>
        </div>

        <div>
            <label>
                City:
                <!--
                     "city" correspond à la propriété city du FormGroup address
                     contenu dans le FormGroup userForm
                -->
                <input type="text" formControlName="city">
            </label>
        </div>

        <div>
            <label>
                Zip code:
                <!--
                     "zipCode" correspond à la propriété zipCode du FormGroup address
                     contenu dans le FormGroup userForm
                -->
                <input type="text" formControlName="zipCode">
            </label>
        </div>
    </div>

    <button type="submit" [disabled]="!userForm.valid">Submit</button>
</form>

Tu peux maintenant regarder de nouveau ce que contient userForm.value lorsque tu cliques sur le bouton submit. Il devrait contenir un objet littéral qui est calqué sur la structure de ton FormGroup userForm.

Challenge

Credentials

Le but de ce challenge est d'ajouter des champs d'authentification dans ton formulaire avec l'email et le mot de passe. Tu stockeras ces champs dans un FormGroup imbriqué à ton FormGroup principal userForm. Tu l'appelleras credentials.

Critères de validation

  • userForm contient un FormGroup imbriqué appelé credentials
  • Le FormGroup credentials contient deux FormControl pour gérer un email et un mot de passe
  • Le FormGroup credentials est bien lié au template html
  • Lors du submit, les credentials apparaissent bien dans une propriété credentials de l'objet littéral userForm.value

Ressources partagées sur cette quête