Le logo de la lib Rxjs

Aujourd’hui on va aborder une phase importante dans la vie d’une application Angular, la gestion desformulaires.

Pour ça, il y a deux possibilités, le formulaire piloté par le template et le formulaire piloté par le code. Nous allons aborder ici les 2 méthodes et les comparer. Nous allons aussi voir pourquoi la partie réactive forms est vraiment la meilleure façon d’aborder les formulaires dans une app Angular.

Le data binding

Avant d’aborder la création de formulaire avec Angular, nous allons parler dû data binding avec Angular, ça nous permettra de mieux comprendre la notion de formulaire piloté par le template.

Nous avons deux types de data-binding, le property binding et l’event binding. Ces 2 types sont différenciés par des caractères au sein du framework. les crochets pour le binding property[attribut] et les parenthèses pour le binding événements (event)

Pour faire simple ce sont tous les deux des bindings de type one ways c'est-à-dire sens unique. Le property binding va du parent vers l’enfant et l’event binding va de l’enfant vers le parent.

Inputs piloté par le template

Une fois cette notion de data-binding comprise, nous allons aborder ensemble la notion de ngmodel lié à angular. Cette notion nous permet de faire du two way data binding, donc binding dans les 2 sens. C'est-à-dire que le parent met au jour l’enfant et l’enfant met à jour le parent. La déclaration de cet attribut est donc un mix des 2 types de binding, [(ngModel)]

Il s’utilise simplement dans le HTML comme ceci

<input [(ngModel)]="user.firstname">

Il suffit de déclarer la propriété dans son composant pour faire la liaison

public user = {
    firstname: '',
    lastname: ''
}

Une fois l’ensemble de nos inputs lié à notre modèle, nous pouvons anticiper la gestion d’erreur. L’utilisation de ngModel, force le développeur à gérer l’entièreté de ses erreurs et de sa validation au travers du HTML et complexifie le template de notre formulaire très rapidement en plus de rendre la validation de champs spéciaux plus compliqués.

<label for="name">firstname</label>

<input type="text" class="form-control" id="firstname"
       required
       [(ngModel)]="model.firstname"
             name="firstname">

<div [hidden]="name.valid || name.pristine" class="alert alert-danger">
  firstname is required
</div>

Inputs piloté par le code

A contrario, avec les reactive forms nous allons pouvoir rendre les formulaires beaucoup plus fun et interactif. Étant piloté par le code, il n’y aura pas de notion de two ways data binding avec son utilisation, leur utilisation est entièrement régie par RXJS.

À l’initialisation du FormControl nous pouvons lui passer plusieurs paramètres. En premier nous avons la valeur par défaut du contrôle et ensuite un tableau qui contiendra l’ensemble desvalidators requis par l’input. (https://angular.io/api/forms/Validators)

public firstname = new FormControl('default value', [Validators.required]);

Concernant le HTML

<label for="name">firstname</label>

<input type="text"
       class="form-control"
       id="firstname"
       required
       [formControl]="firstname">

Le FormControl est un Observable qui nous permet de récupérer les infos de notre input très facilement, il suffit de s’abonner aux modifications de celui-ci et de les récupérer au travers de l’Observable valueChanges.

ngOnInit() {
    this.firstname.valueChanges
        .pipe(
            debounceTime(300),
            tap(console.log)
        )
        .subscribe()
}

Ici, nous avons pris le parti de rajouter un debounceTime de 300ms pour déclencher la récupération des infos 300ms après le dernier appuie sur une touche du clavier, ça évite de surcharger l’émission d’événement à chaque frappe sur le clavier

Afin de garantir que l’entièreté de notre formulaire est validée, nous avons la possibilité de rajouter unFormGroup. Celui-ci va nous permettre de générer nos controls dynamiques en lui passant un JSON par exemple et de centraliser la gestion des erreurs de l’ensemble des validators.

Nous allons donc ajouter cette notion de FormGroup à notre formulaire, il faudra donc modifier le typescript ainsi que le HTML comme ceci

public form: FormGroup = new FormGroup({
    firstname: new FormControl('', [Validators.required]),
    lastname: new FormControl('', [Validatord.required])
});
<form [formGroup]="form">

    <label for="name">firstname</label>
<input type="text" class="form-control" id="firstname"
       required
       FormControlName="firstname">

<label for="lastname">lastname</label>
<input type="text" class="form-control" id="lastname"
       required
       FormControlName="lastname">

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

</form>

Nous allons avoir la possibilité de gérer l’état de notre formulaire de manière globale grâce au FormGroup qui possède 2 propriétés pour définir l’état valid ou invalid de notre formulaire. Nous avons donc une facilité de gestion globale des erreurs de validations et une gestion fine par controls en utilisant la MAP de controls générés aux les initialisations du FormGroup.

<label for="name">firstname</label>

<input type="text" class="form-control" id="firstname"
       required
       [(ngModel)]="model.firstname"
             name="firstname">

<div [hidden]="form.get('firstname').valid" class="alert alert-danger">
  firstname is required
</div>

Grâce à la MAP de controls du FormGroup, nous avons aussi la possibilité de gérer unitairement les controls côté code et donc détecter le changement de valeurs comme nous l’avons vue avant.

ngOnInit() {
    this.form.get('firstname').valueChanges
        .pipe(
            debounceTime(300),
            tap(console.log)
        )
        .subscribe()
}

Conclusion

Nous avons abordé la création des formulaires en Angular et nous avons donc pu voir les différentes façons de faire. Selon le besoin, vous savez maintenant comment gérer vos formulaires et comment devenir efficace dans leurs gestions.