Introduction aux formes angulaires 4 formes réactives

Ce que vous allez créer

Il s’agit de la deuxième partie de la série Introduction aux formes angulaires 4. Dans la première partie, nous avons créé un formulaire à l’aide de l’approche par modèles. Nous avons utilisé des directives telles que ngModel, ngModelGroup et ngForm pour surcharger les éléments de formulaire. Dans ce tutoriel, nous allons adopter une approche différente pour la création de formes: la manière réactive. 

Formes Réactives

Les formulaires réactifs adoptent une approche différente de celle des formulaires basés sur des modèles. Ici, nous créons et initialisons le objets de contrôle de formulaire dans notre classe de composant. Ce sont des objets intermédiaires qui contiennent l'état de la forme. Nous les lierons ensuite au éléments de contrôle de formulaire dans le template.

L'objet de contrôle de formulaire écoute toutes les modifications apportées aux valeurs de contrôle d'entrée et celles-ci sont immédiatement reflétées dans l'état de l'objet. Etant donné que le composant a un accès direct à la structure du modèle de données, toutes les modifications peuvent être synchronisées entre le modèle de données, l'objet de contrôle de formulaire et les valeurs de contrôle d'entrée.. 

En pratique, si nous construisons un formulaire pour la mise à jour du profil utilisateur, le modèle de données est l'objet utilisateur extrait du serveur. Par convention, cela est souvent stocké dans la propriété utilisateur du composant (this.user). L'objet de contrôle de formulaire ou le modèle de formulaire sera lié aux éléments de contrôle de formulaire réels du modèle..

Ces deux modèles devraient avoir des structures similaires, même s'ils ne sont pas identiques. Cependant, les valeurs en entrée ne doivent pas transiter directement dans le modèle de données. L'image décrit comment l'entrée utilisateur du modèle se dirige vers le modèle de formulaire..

Commençons.

Conditions préalables

Vous n'avez pas besoin d'avoir suivi la première partie de cette série pour que la deuxième partie ait un sens. Cependant, si vous êtes novice dans Angular, je vous recommande vivement de suivre la stratégie basée sur des modèles. Le code de ce projet est disponible sur mon référentiel GitHub. Assurez-vous que vous êtes sur la bonne branche, puis téléchargez le zip ou, alternativement, clonez le référentiel pour voir le formulaire en action.. 

Si vous préférez commencer à partir de zéro, assurez-vous d’avoir installé Angular CLI. Utilisez le ng commande pour générer un nouveau projet. 

$ ng new SignupFormProject

Ensuite, générez un nouveau composant pour le SignupForm ou en créer un manuellement. 

ng générer le composant SignupForm

Remplacer le contenu de app.component.html avec ça:

 

Voici la structure de répertoire pour le src / annuaire. J'ai supprimé des fichiers non essentiels pour que tout reste simple.

. ├── app │ ├── app.component.css app.component.html app.component.ts app.module.ts le formulaire d'inscription │ ├ ── signup-form.component.css ├── signup-form.component.html ts signup-form.component.ts └── User.ts ├── index.html main .ts ├── polyfills.ts ├── styles.css ├── tsconfig.app.json typings.d.ts 

Comme vous pouvez le voir, un répertoire pour le SignupForm composant a été créé automatiquement. C'est là que la plupart de notre code ira. J'ai aussi créé un nouveau User.ts pour stocker notre modèle d'utilisateur.

Le modèle HTML

Avant de plonger dans le modèle de composant réel, nous devons avoir une idée abstraite de ce que nous construisons. Donc, voici la structure de forme que j'ai dans mon esprit. Le formulaire d'inscription comportera plusieurs champs de saisie, un élément de sélection et un élément de case à cocher.. 


Voici le modèle HTML que nous allons utiliser pour notre page d'inscription. 

Modèle HTML

 
S'inscrire

Les classes CSS utilisées dans le modèle HTML font partie de la bibliothèque Bootstrap utilisée pour rendre les choses jolies. Comme il ne s’agit pas d’un tutoriel de conception, je ne parlerai pas beaucoup des aspects CSS du formulaire sauf si nécessaire. 

Configuration de base du formulaire

Pour créer un formulaire réactif, vous devez importer le ReactiveFormsModule de @ angulaire / formes et l'ajouter au tableau des importations dans app.module.ts.

app / app.module.ts

// Importer ReactiveFormsModule import ReactiveFormsModule depuis '@ angular / forms'; @NgModule (… // Ajoute le module aux importations Importations de tableaux: [BrowserModule, ReactiveFormsModule…). Classe d'exportation AppModule . 

Ensuite, créez un modèle d’utilisateur pour le formulaire d’inscription. Nous pouvons utiliser une classe ou une interface pour créer le modèle. Pour ce tutoriel, je vais exporter une classe avec les propriétés suivantes.

app / User.ts

classe d'exportation User id: number; email: chaîne; // Les deux mots de passe sont dans un seul objet mot de passe: pwd: string; confirmPwd: string; ; sexe: chaîne; termes: booléen; constructeur (valeurs: Object = ) // Initialisation du constructeur Object.assign (this, values);  

Maintenant, créez une instance du modèle User dans le dossier SignupForm composant. 

app / signup-form / signup-form.component.ts

importer Component, OnInit à partir de '@ angular / core'; // Importe le modèle utilisateur import User depuis './… / User'; @Component (sélecteur: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css']) La classe d'exportation SignupFormComponent implémente OnInit // Liste de genre pour l'élément de contrôle select privé genderList: string []; // Propriété pour l'utilisateur utilisateur privé: User; ngOnInit () this.genderList = ['Homme', 'Femme', 'Autres']; 

Pour le signup-form.component.html fichier, je vais utiliser le même modèle HTML discuté ci-dessus, mais avec des modifications mineures. Le formulaire d'inscription comporte un champ de sélection avec une liste d'options. Bien que cela fonctionne, nous le ferons de manière angulaire en parcourant la liste à l’aide des touches ngFor directif.

app / signup-form / signup-form.component.html

S'inscrire

Remarque: vous pourriez recevoir une erreur disant Aucun fournisseur pour ControlContainer. L'erreur apparaît lorsqu'un composant a une

balise sans directive formGroup. L'erreur disparaîtra une fois que nous aurons ajouté une directive FormGroup plus tard dans le tutoriel..

Nous avons un composant, un modèle et un modèle de formulaire à portée de main. Et maintenant? Il est temps de se salir les mains et de se familiariser avec les API dont vous avez besoin pour créer des formulaires réactifs. Ceci comprend FormControl et FormGroup

Suivi de l'état à l'aide de FormControl

Lors de la création de formulaires avec la stratégie de formulaires réactifs, vous ne rencontrerez pas les directives ngModel et ngForm. Au lieu de cela, nous utilisons les API sous-jacentes FormControl et FormGroup.

Un FormControl est une directive utilisée pour créer une instance de FormControl que vous pouvez utiliser pour garder trace de l'état d'un élément de formulaire particulier et de son statut de validation. Voici comment fonctionne FormControl:

/ * Importer d'abord FormControl * / import FormControl à partir de '@ angular / forms'; / * Exemple de création d'une nouvelle instance de FormControl * / classe d'export SignupFormComponent email = new FormControl (); 

email est maintenant une instance de FormControl et vous pouvez la lier à un élément de contrôle d'entrée de votre modèle comme suit:

S'inscrire

L'élément de formulaire de modèle est maintenant lié à l'instance FormControl dans le composant. Cela signifie que toute modification de la valeur de contrôle d'entrée est reflétée à l'autre extrémité.. 

Un constructeur FormControl accepte trois arguments - une valeur initiale, un tableau de validateurs de synchronisation et un tableau de validateurs asynchrones - et, comme vous l'aurez deviné, ils sont tous facultatifs. Nous allons couvrir les deux premiers arguments ici. 

import Validators de '@ angular / forms';… / * FormControl avec une valeur initiale et un validateur * / email = new FormControl ('[email protected] ', Validators.required); 

Angular a un ensemble limité de validateurs intégrés. Les méthodes de validation populaires incluent Validateurs.requis, Validators.minLength, Validators.maxlength, et Validators.pattern. Cependant, pour les utiliser, vous devez d'abord importer l'API Validator..

Pour notre formulaire d'inscription, nous avons plusieurs champs de contrôle de saisie (email et mot de passe), un champ de sélection et un champ de case à cocher. Plutôt que de créer un individu FormControl objets, ne serait-il pas plus logique de regrouper tous ces FormControlest sous une seule entité? Ceci est avantageux car nous pouvons maintenant suivre la valeur et la validité de tous les objets sous-FormControl à un endroit. C'est ce que FormGroup est pour. Nous allons donc enregistrer un groupe de formulaires parent avec plusieurs enfants FormControls. 

Grouper plusieurs FormControls avec FormGroup

Pour ajouter un groupe de formulaires, importez-le d'abord. Ensuite, déclarez signupForm en tant que propriété de classe et initialisez-le comme suit:

app / signup-form / signup-form.component.ts

// Importe l'API pour la construction d'un formulaire import FormControl, FormGroup, Validators from '@ angular / forms'; La classe d'exportation SignupFormComponent implémente OnInit genderList: String []; signupForm: FormGroup;… ngOnInit () this.genderList = ['Homme', 'Femme', 'Autres']; this.signupForm = new FormGroup (email: new FormControl (", Validators.required)), pwd: new FormControl (), confirmPwd: new FormControl (), sexe: nouveau FormControl (), termes: new FormControl ()) 

Liez le modèle FormGroup au DOM comme suit: 

app / signup-form / signup-form.component.html

  
S'inscrire

[formGroup] = "signupForm" indique à Angular que vous souhaitez associer ce formulaire au FormGroup déclarée dans la classe de composant. Quand angulaire voit formControlName = "email", il recherche une instance de FormControl avec la valeur de clé email à l'intérieur du groupe de formulaires parent. 

De même, mettez à jour les autres éléments du formulaire en ajoutant un formControlName = "valeur" attribuer comme nous venons de le faire ici.

Pour voir si tout fonctionne comme prévu, ajoutez ce qui suit après la balise de formulaire:

app / signup-form / signup-form.component.html

 

Valeur de formulaire signupForm.value | json

Statut du formulaire signupForm.status | json

Pipe le SignupForm propriété à travers le JsonPipe rendre le modèle au format JSON dans le navigateur. Ceci est utile pour le débogage et la journalisation. Vous devriez voir une sortie JSON comme celle-ci.

Il y a deux choses à noter ici:

  1. Le JSON ne correspond pas exactement à la structure du modèle utilisateur créé précédemment. 
  2. le signupForm.status indique que l'état du formulaire est INVALID. Cela montre clairement que le Validateurs.requis sur le champ de contrôle de messagerie fonctionne comme prévu. 

La structure du modèle de formulaire et le modèle de données doivent correspondre. 

// Modèle de formulaire "email": "", "pwd": "", "confirmPwd": "", "genre": "", "termes": false // modèle d'utilisateur "email": "" , "mot de passe": "pwd": "", "confirmPwd": "",, "genre": "", "termes": faux

Pour obtenir la structure hiérarchique du modèle de données, nous devons utiliser un groupe de formulaires imbriqué. De plus, il est toujours judicieux d’avoir des éléments de formulaire liés dans un seul groupe de formulaires.. 

Groupe de formulaires imbriqué

Créer un nouveau groupe de formulaires pour le mot de passe.

app / signup-form / signup-form.component.ts

 this.signupForm = new FormGroup (email: new FormControl (", Validators.required)), mot de passe: new FormGroup (pwd: new FormControl (), confirmPwd: nouveau FormControl ()), genre: new FormControl (), conditions : new FormControl ())

Maintenant, pour lier le nouveau modèle de formulaire au DOM, apportez les modifications suivantes:

app / signup-form / signup-form.component.html

 

formGroupName = "mot de passe" effectue la liaison pour le groupe de formes imbriqué. Maintenant, la structure du modèle de formulaire correspond à nos exigences.

Valeur du formulaire: "email": "", "mot de passe": "pwd": null, "confirmPwd": null, "genre": null, "termes": null Etat du formulaire "INVALID"

Ensuite, nous devons valider les contrôles de formulaire.

Valider le formulaire

Nous avons mis en place une validation simple pour le contrôle des entrées de courrier électronique. Cependant, cela ne suffit pas. Voici la liste complète de nos exigences pour la validation.

  • Tous les éléments de contrôle de formulaire sont Champs obligatoires.
  • Désactiver le bouton d'envoi jusqu'à ce que le statut du formulaire soit VALIDE.
  • Le champ email doit contenir strictement un identifiant.
  • Le mot de passe doit avoir une longueur minimale de 8.

Le premier est facile. Ajouter Validator.required à tous les FormControls dans le modèle de formulaire. 

app / signup-form / signup-form.component.ts 

 this.signupForm = new FormGroup (email: new FormControl (", Validators.required), mot de passe: new FormGroup (pwd: new FormControl (", Validators.required)) ", confirmPwd: new FormControl (", Validators.required) ), genre: new FormControl (", Validators.required), // requiredTrue afin que le champ de termes ne soit valide que si les termes cochés sont: new FormControl (", Validators.requiredTrue))

Ensuite, désactivez le bouton lorsque le formulaire est INVALID.

app / signup-form / signup-form.component.html

 

Pour ajouter une contrainte sur le courrier électronique, vous pouvez utiliser le paramètre par défaut. Validators.email ou créer une coutume Validators.pattern () qui spécifie des expressions régulières comme celle ci-dessous:

email: new FormControl (", [Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0-9 .-] + \. [az] 2,3  $ ')])

Utilisez le Longueur minimale validateur pour les champs de mot de passe.

 password: new FormGroup (pwd: new FormControl (", Validators.required, Validators.minLength (8)]), confirmPwd: new FormControl (", [Validators.required, Validators.minLength (8)])),

Voilà pour la validation. Cependant, la logique du modèle de formulaire semble encombrée et répétitive. Nettoyons cela en premier. 

Refactoriser le code à l'aide de FormBuilder

Angular vous fournit un sucre de syntaxe pour la création de nouvelles instances de FormGroup et FormControl appelé FormBuilder. L'API FormBuilder ne fait rien de spécial autre que ce que nous avons couvert ici.

Cela simplifie notre code et simplifie le processus de création d'un formulaire. Pour créer un FormBuilder, vous devez l'importer dans signup-form.component.ts et injecter le FormBuilder dans le constructeur.

app / signup-form / signup-form.component.ts 

import FormBuilder, FormGroup, Validators from '@ angular / forms';… la classe d'exportation SignupFormComponent implémente OnInit signupForm: FormGroup; // Déclarer le formulaire de signature // Injecter le constructeur de formes dans le constructeur du constructeur (fb privé: FormBuilder)  ngOnInit () …

Au lieu de créer un nouveau FormGroup (), nous utilisons this.fb.group construire un formulaire. À l'exception de la syntaxe, tout le reste reste le même.

app / signup-form / signup-form.component.ts 

 ngOnInit () … this.signupForm = this.fb.group (email: [", Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0- 9 .-] + \. [Az] 2,3 $ ')], mot de passe: this.fb.group (pwd: [", [Validators.required, Validators.minLength (8)]], confirmPwd : [", [Validators.required, Validators.minLength (8)]]), genre: [", Validators.required], termes: [", Validators.requiredTrue])

Affichage des erreurs de validation 

Pour afficher les erreurs, je vais utiliser la directive conditionnelle ngIf sur un élément div. Commençons par le champ de contrôle de saisie pour le courrier électronique:

 

Il y a quelques problèmes ici. 

  1. D'où vient invalide et parfait viens de? 
  2. signupForm.controls.email.invalid est trop long et profond.
  3. L'erreur ne dit pas explicitement pourquoi elle est invalide.

Pour répondre à la première question, chaque FormControl a certaines propriétés comme invalide, valide, parfait, sale, touché, et intacte. Nous pouvons les utiliser pour déterminer si un message d'erreur ou un avertissement doit être affiché ou non. L'image ci-dessous décrit chacune de ces propriétés en détail.

Donc, l'élément div avec le * ngIf sera rendu que si l'email est invalide. Cependant, l'utilisateur sera accueilli avec des erreurs sur le fait que les champs de saisie sont vides avant même d'avoir la possibilité de modifier le formulaire.. 

Pour éviter ce scénario, nous avons ajouté la deuxième condition. L'erreur ne sera affichée qu'après le contrôle a été visité.

Pour se débarrasser de la longue chaîne de noms de méthodes (signupForm.controls.email.invalid), Je vais ajouter quelques méthodes de getter abrégées. Cela les rend plus accessibles et courts. 

app / signup-form / signup-form.component.ts 

La classe d'exportation SignupFormComponent implémente OnInit … get email () return this.signupForm.get ('email');  get password () return this.signupForm.get ('password');  get gender () return this.signupForm.get ('gender');  get terms () return this.signupForm.get ('terms'); 

Pour rendre l'erreur plus explicite, j'ai ajouté les conditions ngIf imbriquées ci-dessous:

app / signup-form / signup-form.component.html

 
Le champ email ne peut pas être vide
L'identifiant email ne semble pas correct

Nous utilisons email.errors pour vérifier toutes les erreurs de validation possibles puis les réafficher à l'utilisateur sous forme de messages personnalisés. Suivez maintenant la même procédure pour les autres éléments de formulaire. Voici comment j'ai codé la validation des mots de passe et les termes contrôle de saisie.

app / signup-form / signup-form.component.html

  
Le mot de passe doit comporter plus de 8 caractères.
Veuillez accepter les termes et conditions d'abord.

Soumettez le formulaire en utilisant ngSubmit

Nous avons presque fini avec le formulaire. Il manque la fonctionnalité d'envoi que nous sommes sur le point d'implémenter maintenant.

Lors de la soumission du formulaire, les valeurs du modèle de formulaire doivent être insérées dans la propriété utilisateur du composant..

app / signup-form / signup-form.component.ts

public onFormSubmit () if (this.signupForm.valid) this.user = this.signupForm.value; console.log (this.user); / * Toute logique d'appel d'API via des services va ici * /

Envelopper

Si vous suivez ce tutoriel depuis le début, nous avons eu une expérience pratique de deux technologies populaires de création de formulaires dans Angular. Les techniques basées sur des modèles et sur des modèles sont deux manières de réaliser la même chose. Personnellement, je préfère utiliser les formes réactives pour les raisons suivantes:

  • Toute la logique de validation de formulaire sera située à un endroit unique, à l'intérieur de votre classe de composants. C'est beaucoup plus productif que l'approche de modèle, où les directives ngModel sont dispersées à travers le modèle.
  • Contrairement aux formulaires basés sur des modèles, les formulaires basés sur un modèle sont plus faciles à tester. Vous n'avez pas besoin de recourir à des bibliothèques de test de bout en bout pour tester votre formulaire..
  • La logique de validation ira à l'intérieur de la classe de composant et non dans le modèle.
  • Pour un formulaire avec un grand nombre d'éléments de formulaire, cette approche s'appelle FormBuilder pour faciliter la création d'objets FormControl..

Nous avons manqué une chose: écrire un validateur pour la non-concordance des mots de passe. Dans la dernière partie de la série, nous aborderons tout ce que vous devez savoir sur la création de fonctions de validation personnalisées dans Angular. Restez à l'écoute d'ici là.

En attendant, il existe de nombreux frameworks et bibliothèques pour vous occuper, avec de nombreux articles à lire, étudier et utiliser sur le marché Envato..