📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

~2 min de lecture

Angular 19 — Form Value Control : fini le ControlValueAccessor verbeux

Jusqu'ici, créer un composant de formulaire réutilisable imposait d'implémenter ControlValueAccessor. Résultat : un provider à déclarer, trois méthodes obligatoires à câbler, et la gestion manuelle du disabled avec un markForCheck au moindre oubli.

Angular 19 introduit une alternative Signal-first : FormValueControl. Moins de lignes, moins de risques, et le comportement disabled géré automatiquement.


TL;DR

ControlValueAccessor FormValueControl
Provider NG_VALUE_ACCESSOR requis Aucun
API 3–4 méthodes manuelles 1 model()
Disabled setDisabledState + markForCheck Automatique
Version Toutes Angular 19+

L'ancienne façon — ControlValueAccessor

Pour illustrer, voici un sélecteur de quantité classique :

@Component({
  selector: 'app-quantity',
  template: `
    <button (click)="decrement()">-</button>
    <span>{{ qty }}</span>
    <button (click)="increment()">+</button>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => QuantityComponent),
      multi: true,
    },
  ],
})
export class QuantityComponent implements ControlValueAccessor {
  protected qty = 0;

  private onChange: (v: number) => void = () => {};
  private onTouched: () => void = () => {};

  writeValue(value: number): void {
    this.qty = value ?? 0;
  }

  registerOnChange(fn: (v: number) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    // il faut penser à injecter ChangeDetectorRef et appeler markForCheck()
  }

  increment(): void {
    this.qty++;
    this.onChange(this.qty);
    this.onTouched();
  }

  decrement(): void {
    this.qty--;
    this.onChange(this.qty);
    this.onTouched();
  }
}

Beaucoup de plomberie pour un comportement assez simple.


La nouvelle façon — FormValueControl

Avec Angular 19, le même composant devient :

import { FormValueControl } from '@angular/forms';

@Component({
  selector: 'app-quantity',
  template: `
    <button (click)="decrement()" [disabled]="disabled()">-</button>
    <span>{{ value() }}</span>
    <button (click)="increment()" [disabled]="disabled()">+</button>
  `,
})
export class QuantityComponent implements FormValueControl<number> {
  public value = model<number | null>(0);
  public disabled = model<boolean>(false);

  increment(): void {
    if (!this.disabled()) this.value.update((v) => (v ?? 0) + 1);
  }

  decrement(): void {
    if (!this.disabled()) this.value.update((v) => (v ?? 0) - 1);
  }
}
  • Plus de providers à déclarer
  • Plus de callbacks onChange / onTouched à stocker
  • disabled se synchronise automatiquement avec l'état du formulaire parent

Important : value et disabled doivent être déclarés public pour satisfaire le contrat de l'interface — une déclaration protected produit une erreur de compilation.


Côté parent — la structure formulaire

FormValueControl impose d'encapsuler le contrôle dans un objet de formulaire. Un FormControl isolé ne suffit plus :

@Component({
  template: `
    <form [formGroup]="form">
      <app-quantity formControlName="qty" />
    </form>
    <button (click)="toggle()">Activer / Désactiver</button>
    <p>Valeur : {{ form.value.qty }}</p>
  `,
})
export class CartComponent {
  protected readonly form = new FormGroup({
    qty: new FormControl(1),
  });

  toggle(): void {
    const ctrl = this.form.controls.qty;
    ctrl.disabled ? ctrl.enable() : ctrl.disable();
  }
}

Appuie sur le bouton → le composant enfant reflète instantanément l'état disabled, sans aucune détection de changement manuelle.


Types spécialisés

Angular fournit déjà quelques interfaces dérivées pour les cas courants :

  • FormCheckboxControl — pour les cases à cocher (value: ModelSignal<boolean | null>)
  • FormSelectControl<T> — pour les listes déroulantes

Si ton composant correspond à l'un de ces types, utilise l'interface spécialisée plutôt que FormValueControl<T> générique.


Faut-il migrer tout de suite ?

Pas forcément. ControlValueAccessor reste pleinement supporté et n'est pas deprecated. La migration a du sens si :

  • tu crées un nouveau composant de formulaire (pas de raison de partir sur l'ancienne API)
  • tu as un composant existant déjà bien testé et sans bug → reste sur CVA, la valeur ajoutée ne justifie pas le risque
  • tu veux réduire la friction pour les développeurs juniors de ton équipe → FormValueControl gagne clairement

Conclusion

FormValueControl élimine le principal point de friction de la création de composants personnalisés : plus de providers, plus de callbacks, plus de détection de changement manuelle. L'interface est courte, lisible, et tire pleinement parti des Signals.

Si tu avais l'habitude de contourner ControlValueAccessor par crainte de la complexité, c'est le bon moment pour reconsidérer la chose.


Tu veux aller plus loin et maîtriser Angular de fond en comble — Signals, formulaires, architecture, tests ? C'est exactement ce que couvre EasyAngularKit, la formation Angular par la pratique.

📧 Reste informé(e) !

Reçois les derniers articles et conseils EasyAngularKit directement dans ta boîte mail.

S'inscrire gratuitement

AngularKit

Suite d'outils pour développeurs Angular francophones. Apprends, modernise tes réflexes, audite ta codebase.

Produits

Contact

Légal

© 2026 AngularKit. Tous droits réservés.