15 - Les formulaires : Reactive Form et FormGroup
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
FormControldans unFormGroup - 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.
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.
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.
<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 :
<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>
<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.
...
@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 :
...
// 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 :
...
// 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.
<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
userFormcontient unFormGroupimbriqué appelécredentials- Le
FormGroupcredentialscontient deuxFormControlpour gérer un email et un mot de passe - Le
FormGroupcredentialsest bien lié au template html - Lors du submit, les credentials apparaissent bien dans une propriété
credentialsde l'objet littéraluserForm.value
Ressources partagées sur cette quête
- doc officielle a garder sous le coudehttps://angular.io/guide/reactive-forms#reactive-forms