Skip to content

Comment utiliser ControlValueAccessor ?

Pour utiliser ControlValueAccessor pour créer une palette de couleurs sur Angular, vous pouvez suivre ces étapes :

  1. Vous devez créer un composant de palette de couleurs qui implémente l'interface ControlValueAccessor. Cela vous permet de contrôler la valeur du composant et de déclencher des événements lorsque la valeur change. Voici un exemple de code pour un composant de palette de couleurs :
ts
import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-color-picker',
  template: `
    <div class="color-picker">
      <div
        *ngFor="let color of colors"
        [style.background]="color"
        class="color"
        (click)="selectColor(color)"
        [class.selected]="color === selectedColor"
      ></div>
    </div>
  `,
  styles: [
    `
      .color-picker {
        display: flex;
      }
      .color {
        width: 20px;
        height: 20px;
        border: 1px solid #ccc;
      }
      .color.selected {
        border: 2px solid #000;
      }
    `,
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ColorPickerComponent),
      multi: true,
    },
  ],
})
export class ColorPickerComponent implements ControlValueAccessor {
  @Input() colors: string[] = [];

  selectedColor: string;

  onChange: (value: string) => void;
  onTouched: () => void;

  writeValue(value: string): void {
    this.selectedColor = value;
  }

  registerOnChange(fn: (value: string) => void): void {
    this.onChange = fn;
  }

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

  selectColor(color: string) {
    this.selectedColor = color;
    this.onChange(color);
  }
}
import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-color-picker',
  template: `
    <div class="color-picker">
      <div
        *ngFor="let color of colors"
        [style.background]="color"
        class="color"
        (click)="selectColor(color)"
        [class.selected]="color === selectedColor"
      ></div>
    </div>
  `,
  styles: [
    `
      .color-picker {
        display: flex;
      }
      .color {
        width: 20px;
        height: 20px;
        border: 1px solid #ccc;
      }
      .color.selected {
        border: 2px solid #000;
      }
    `,
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => ColorPickerComponent),
      multi: true,
    },
  ],
})
export class ColorPickerComponent implements ControlValueAccessor {
  @Input() colors: string[] = [];

  selectedColor: string;

  onChange: (value: string) => void;
  onTouched: () => void;

  writeValue(value: string): void {
    this.selectedColor = value;
  }

  registerOnChange(fn: (value: string) => void): void {
    this.onChange = fn;
  }

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

  selectColor(color: string) {
    this.selectedColor = color;
    this.onChange(color);
  }
}

NG_VALUE_ACCESSOR est une constante exportée par Angular qui définit un token de fournisseur de valeur. Elle est utilisée pour fournir une valeur à une directive ou un composant qui implémente l'interface ControlValueAccessor.

Nous utilisons NG_VALUE_ACCESSOR dans la définition de fournisseur du composant ColorPickerComponent. Cela indique à Angular que ce composant implémente l'interface ControlValueAccessor et qu'il peut être utilisé comme un contrôle de formulaire.

La propriété multi: true de la définition de fournisseur indique à Angular qu'il doit créer une nouvelle instance du fournisseur pour chaque demande de fournisseur. Cela est utile lorsque vous avez plusieurs instances du même composant ou directive dans votre application et que vous voulez que chaque instance ait sa propre valeur. Si vous ne définissez pas multi: true, Angular utilisera la même instance de fournisseur pour toutes les demandes.

Nous avons ensuite des méthodes à implémenter:

  • writeValue(value: any) : Cette méthode est appelée par Angular lorsqu'il souhaite mettre à jour la valeur du composant avec une nouvelle valeur. Dans l'exemple, nous mettons simplement à jour la propriété selectedColor avec la valeur donnée.

  • registerOnChange(fn: any) : Cette méthode est appelée par Angular pour enregistrer une fonction à appeler lorsque la valeur du composant change. Dans l'exemple, nous enregistrons simplement la fonction donnée dans la propriété onChange.

  • registerOnTouched(fn: any) : Cette méthode est appelée par Angular pour enregistrer une fonction à appeler lorsque le composant est "touche" (par exemple lorsque l'utilisateur clique sur le composant). Dans l'exemple, nous enregistrons simplement la fonction donnée dans la propriété onTouched.

En utilisant ces méthodes, vous pouvez contrôler la valeur du composant et déclencher des événements lorsque la valeur change ou lorsque le composant est "touche". Cela vous permet de créer des composants de formulaire personnalisés qui s'intègrent parfaitement au système de formulaire d'Angular.

Comment utiliser dans le template HTML avec ngModel ?

Pour utiliser le composant ColorPickerComponent dans le template HTML avec ngModel, vous devez d'abord importer FormsModule dans votre module Angular :

ts
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [FormsModule],
  // ...
})
export class MyModule {}
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [FormsModule],
  // ...
})
export class MyModule {}

Ensuite, vous pouvez utiliser ColorPickerComponent dans votre template HTML comme n'importe quel autre composant de formulaire en utilisant la directive ngModel :

html
<form>
  <app-color-picker [(ngModel)]="selectedColor"></app-color-picker>
</form>
<form>
  <app-color-picker [(ngModel)]="selectedColor"></app-color-picker>
</form>

Dans cet exemple, nous utilisons la syntaxe de liaison de données bidirectionnelle [(ngModel)] pour lier la valeur du composant ColorPickerComponent à la propriété selectedColor de notre composant. Cela signifie que lorsque la valeur du composant change, la propriété selectedColor sera mise à jour et inversement.

Qu'est ce c'est forwardRef ?

forwardRef est une fonction Angular qui vous permet de référencer un composant ou une directive qui n'a pas encore été déclaré. Elle est souvent utilisée lorsque vous avez besoin de référencer un composant ou une directive qui se trouve dans un autre fichier ou dans le même fichier, mais qui est déclaré plus tard.

Voici un exemple de code qui utilise forwardRef :

ts
import { forwardRef, Directive } from '@angular/core';

@Directive({
  selector: '[appMyDirective]',
  providers: [
    {
      provide: 'someToken',
      useExisting: forwardRef(() => MyDirective),
    },
  ],
})
export class MyDirective {
  // ...
}
import { forwardRef, Directive } from '@angular/core';

@Directive({
  selector: '[appMyDirective]',
  providers: [
    {
      provide: 'someToken',
      useExisting: forwardRef(() => MyDirective),
    },
  ],
})
export class MyDirective {
  // ...
}

Dans cet exemple, nous utilisons forwardRef dans la propriété useExisting de la définition de fournisseur de la directive. Cela nous permet de référencer la directive MyDirective avant qu'elle ne soit déclarée.