# Comment créer facilement un composant input réutilisable piloté par l'interface ControlValueAccessor ?

L'une des problématiques majeures en Angular est de créer des composants réutilisables ce qui constitue l'essence même du framework. Souvent, nous sommes amenés à créer des composants `input` pour les utiliser un peu partout dans nos projets suivant le besoin. Ces composants `input` doivent être fonctionnels lorsque nous mettons en place un formulaire piloté par un Template-Driven Form ou un Reactive Form.

Le but de cet article est de mettre la lumière sur comment créer facilement un composant `input` réutilisable que nous pourrions utiliser dans nos formulaires réactifs.

**Etape 1 : Création du composant** `app-input`

Commençons par générer un nouveau composant avec la commande Angular CLI :

```bash
  ng generate component input
```

Nous modifierons le template de notre composant pour avoir ceci de façon très basique :

```xml
  <input type="text" [formControl]="control"/>
```

**Etape 2: Implémentation de** `ControlValueAccessor`

Pour que notre composant puisse être utilisé dans un formulaire réactif ou un formulaire basé sur des modèles, nous devons implémenter l'interface `ControlValueAccessor`.

Nous avons besoin de l'interface `ControlValueAccessor` parce que lorsqu'on crée le composant et qu'on désire l'utiliser dans un composant parent, nous ne devons pas oublier qu'il sera encapsulé dans son sélecteur `app-input` (dans notre cas ici). Ainsi, tous les attributs auxquels l'on avait accès directement sur un `input` ne seront plus disponibles. C'est davantage plus difficile si l'on veut avoir le contrôle sur notre composant via un formulaire réactif ou un formulaire basé sur des modèles.

Cette interface fournit une série de méthodes que nous pouvons utiliser pour synchroniser la valeur de notre composant avec un FormControl.

Nous pouvons le faire en ajoutant les méthodes suivantes à notre composant :

```typescript
onChange: any = () => {};

onTouched = () => {};

writeValue(value: any): void {
    // mettre à jour la valeur de votre composant
}

registerOnChange(fn: any): void {
 // enregistrer une fonction qui sera appelée lorsque la valeur du composant change
 this.onChange = fn;
}

registerOnTouched(fn: any): void {
 // enregistrer une fonction qui sera appelée lorsque le composant est touché
 this.onTouched = fn;
}
```

Lorsqu'on implémente l'interface `ControlValueAccessor`, ces trois méthodes sont automatiquement redéfinies dans notre composant. Ces méthodes permettront à notre composant d'écouter les changements et de les enregistrer suivant le besoin.

Il faut aussi penser à rendre accessible l'objet `AbstractControl` afin de pouvoir effectuer des manipulations dans le template `HTML` de notre composant pour soit afficher des messages d'erreur ou gérer des scénarios. Voici ce qu'il faut faire :

```typescript
...

public control: AbstractControl;

...

constructor(private injector: Injector) {}

ngAfterViewInit(): void {
    const ngControl = this.injector.get(NgControl, null);
    if (ngControl) {
      setTimeout(() => {
        this.control = Object.assign(new FormControl(), ngControl.control) as FormControl;
      });
    }
  }
```

Cette initialisation dans la méthode `ngAfterViewInit` nous permet d'accéder facilement à l'objet `AbstractControl` de notre composant `input` et de prévenir un problème lié à l'appel de notre composant dans un composant parent. En effet, le `setTimeout` est utilisé pour s'assurer que le `DOM` a été bien initialisé par le parent pour permettre l'utilisation de notre composant enfant.

Nous devons également fournir un provider pour notre composant qui indique à Angular d'utiliser notre composant comme un `ControlValueAccessor` :

```typescript
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputComponent),
      multi: true
    }
  ]
```

**Etape 3 : Utilisation du composant dans un formulaire**

Maintenant que notre composant est un `ControlValueAccessor`, nous pouvons l'utiliser dans un formulaire réactif. Voici un exemple :

```xml
  <form [formGroup]="form">
    <app-input formControlName="firstname"></app-input>
  </form>
```

Nous pouvons obtenir la valeur du composant en utilisant `form.get('firstname').value` et nous pouvons définir la valeur du composant en utilisant `form.get('firstname').setValue(value)`.

> Est-ce que ça fonctionne aussi avec `[(ngModel)]` ?

La question de savoir si `ngModel` est pris en compte par le `ControlValueAccessor` est très récurrente !

La réponse est `OUI`. Nous pouvons utiliser `ngModel` sur un composant piloté par `ControlValueAccessor` et ça fonctionne parfaitement :

```xml
 <app-input [(ngModel)]="value"></app-input>
```

En somme, lorsque nous décidons de créer des composants réutilisables de type `input`, pensons toujours à implémenter l'interface `ControlValueAccessor` pour avoir cette flexibilité à pouvoir l'utiliser dans un formulaire réactif (Reactive Form) ou un formulaire basé sur des modèles (Template-Driven Form).

> Pour plus d'informations :
> 
> 👉 [ControlValueAccessor](https://angular.dev/api/forms/ControlValueAccessor)
> 
> 👉 [Reactive Forms](https://angular.dev/guide/forms/reactive-forms)
> 
> 👉 [Template-Driven Forms](https://angular.dev/guide/forms/template-driven-forms)
