Angular est-il vraiment plus compliqué que React ?

Quand on débute avec Angular, il est facile de se sentir découragé face à la multitude de concepts à assimiler. Cette complexité peut inciter à se tourner vers des frameworks comme React, qui semblent plus simples à première vue. Mais est-ce vraiment le cas ?

Abonnez-vous à notre chaîne

Pour profiter des prochaines vidéos sur Angular, abonnez-vous à la nouvelle chaîne YouTube !

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

FormArray : gérer des champs de formulaire dynamiques

Les FormArray sont particulièrement utiles lorsque vous devez gérer une liste dynamique de champs de formulaire. Imaginez que vous créez une liste de courses : vous ne savez pas à l'avance combien d'articles vous allez ajouter, et vous voulez pouvoir en ajouter ou en supprimer facilement.

Comprendre par l'exemple

Prenons un exemple concret : vous gérez un formulaire d'inscription à un événement où les participants peuvent ajouter plusieurs invités. Chaque invité a un nom et une adresse email.

Idée principale

FormArray est idéal quand vous ne connaissez pas à l'avance le nombre d'éléments que l'utilisateur va ajouter.

Voici comment implémenter cela :

ts
import { Component, inject } from '@angular/core';
import {
  FormArray,
  FormBuilder,
  ReactiveFormsModule,
  Validators,
} from '@angular/forms';

@Component({
  selector: 'app-event-form',
  standalone: true,
  templateUrl: './event-form.component.html',
  imports: [ReactiveFormsModule],
})
export class EventFormComponent {
  private fb = inject(FormBuilder);
  eventForm = this.fb.group({
    eventName: ['', Validators.required],
    guests: this.fb.array([]),
  });

  get guests() {
    return this.eventForm.get('guests') as FormArray;
  }

  addGuest() {
    const guestForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
    });

    this.guests.push(guestForm);
  }

  removeGuest(index: number) {
    this.guests.removeAt(index);
  }

  onSubmit() {
    if (this.eventForm.valid) {
      console.log(this.eventForm.value);
    }
  }
}
angular-html
<form [formGroup]="eventForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="eventName">Nom de l'événement</label>
    <input id="eventName" formControlName="eventName" />
  </div>

  <div formArrayName="guests">
    @for (guest of guests.controls; track $index) {
    <div [formGroupName]="$index">
      <h3>Invité #{{ $index + 1 }}</h3>

      <div>
        <label>Nom:</label>
        <input formControlName="name" />
      </div>

      <div>
        <label>Email:</label>
        <input formControlName="email" />
      </div>

      <button type="button" (click)="removeGuest($index)">
        Supprimer l'invité
      </button>
    </div>
    }
  </div>

  <button type="button" (click)="addGuest()">Ajouter un invité</button>

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

Comment fonctionne FormArray ?

Analysons chaque partie :

1. Création du FormArray

STRUCTURE

Un FormArray est toujours une propriété d'un FormGroup parent. Il peut contenir des FormControl, FormGroup ou même d'autres FormArray.

ts
eventForm = this.fb.group({
  eventName: ['', Validators.required],
  guests: this.fb.array([]), // FormArray vide au départ
});

2. Accès au FormArray

Pour manipuler facilement le FormArray, on crée un getter :

ts
get guests() {
  return this.eventForm.get('guests') as FormArray;
}

3. Ajout d'éléments

Quand on ajoute un élément, on crée généralement un nouveau FormGroup :

ts
const guestForm = this.fb.group({
  name: ['', Validators.required],
  email: ['', [Validators.required, Validators.email]],
});

this.guests.push(guestForm);

4. Structure HTML

La directive formArrayName permet de lier un élément HTML à un FormArray dans votre formulaire réactif. Elle agit comme un pont entre votre template et la définition du FormArray dans votre composant.

Voici comment elle fonctionne :

ts
eventForm = this.fb.group({
  eventName: [''],
  guests: this.fb.array([]) // 'guests' est le nom qu'on utilisera dans formArrayName
});
angular-html
<div formArrayName="guests">
  <!-- Ici, Angular sait que 'guests' fait référence au FormArray -->
  <!-- Le contenu de cette div a accès au contexte du FormArray -->
</div>

[formGroupName]="$index" permet d'accéder à chaque FormGroup individuel dans le FormArray. L'index est crucial car il indique à Angular quel élément du tableau nous manipulons.

Voici un exemple détaillé :

ts
// Dans le composant, chaque élément du FormArray est un FormGroup
addGuest() {
  const guestForm = this.fb.group({
    name: [''],
    email: ['']
  });
  this.guests.push(guestForm);
}
angular-html
<div formArrayName="guests">
  @for (guest of guests.controls; track $index) {
    <!-- $index représente la position dans le tableau (0, 1, 2, etc.) -->
    <div [formGroupName]="$index">
      <!-- Maintenant nous sommes dans le contexte du FormGroup spécifique -->
      <input formControlName="name">
      <input formControlName="email">
    </div>
  }
</div>

Hiérarchie des directives

La structure hiérarchique est importante :

angular-html
<form [formGroup]="eventForm">              <!-- Niveau 1: FormGroup principal -->
  <div formArrayName="guests">              <!-- Niveau 2: FormArray -->
    <div [formGroupName]="$index">          <!-- Niveau 3: FormGroup individuel -->
      <input formControlName="name">        <!-- Niveau 4: FormControl -->
    </div>
  </div>
</form>

ATTENTION

Les directives doivent respecter cette hiérarchie. Si vous oubliez un niveau ou changez l'ordre, Angular lancera une erreur.

Accès aux valeurs et états

Vous pouvez accéder aux valeurs et états à différents niveaux :

ts
// Accès à tout le FormArray
console.log(this.guests.value);

// Accès à un FormGroup spécifique
console.log(this.guests.at(0).value);

// Accès à un contrôle spécifique
console.log(this.guests.at(0).get('name').value);

// Vérification de la validité
console.log(this.guests.valid); // Vérifie tout le FormArray
console.log(this.guests.at(0).valid); // Vérifie un FormGroup spécifique

ASTUCE

Utilisez la méthode at() plutôt que l'accès direct par index (controls[index]) car elle est plus sûre et typée.

5. Méthodes utiles du FormArray

Voici les principales méthodes que vous pouvez utiliser avec FormArray :

ts
// Ajouter un contrôle à la fin
this.guests.push(newFormGroup);

// Ajouter un contrôle à une position spécifique
this.guests.insert(index, newFormGroup);

// Supprimer un contrôle à un index
this.guests.removeAt(index);

// Supprimer tous les contrôles
this.guests.clear();

// Obtenir le nombre de contrôles
const length = this.guests.length;

Exemple pratique : Validation du nombre d'invités

Voici comment ajouter une validation sur le nombre d'invités :

ts
addGuest() {
  if (this.guests.length < 5) {  // Maximum 5 invités
    const guestForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
    });
    
    this.guests.push(guestForm);
  }
}
angular-html
<button 
  type="button" 
  (click)="addGuest()" 
  [disabled]="guests.length >= 5">
  Ajouter un invité
</button>

@if (guests.length >= 5) {
  <p class="error">Maximum 5 invités autorisés</p>
}

Accès aux valeurs

Pour récupérer toutes les valeurs du FormArray :

ts
onSubmit() {
  if (this.eventForm.valid) {
    const guestList = this.guests.value; // Tableau des valeurs
    console.log(guestList);
    // [{name: "John", email: "[email protected]"}, ...]
  }
}

Chaque mois, recevez en avant-première notre newsletter avec les dernières actualités, tutoriels, astuces et ressources Angular directement par email !