Model Input : Un aperçu approfondi

Photo by Matt Foxx on Unsplash

Model Input : Un aperçu approfondi

Dans notre précédent article, nous avons vu ensemble ce qu'est un Signal Input et comment l'utiliser. Nous avons particulièrement remarqué sa similarité avec le décorateur @Input().

Dans ce nouvel article, nous verrons ensemble un nouveau type de input qui est toujours basé sur Angular Signal. Ce nouveau type de input a été conçu de façon très ingénieuse et est aussi très similaire à une fonctionnalité existante de Angular qui est très utilisée. Découvrons-le ensemble sans plus attendre !

Qu'est-ce qu'un Model Input en Angular ?

En nous basant sur la documentation de Angular, un Model Input est un type spécial d'input qui permet à un composant A de propager de nouvelles valeurs vers un composant B. En effet, lorsqu'on analyse cette définition, ce qui nous vient à l'esprit c'est qu'un Model Input fait la même chose qu'un Signal Input dans le fonctionnement comme l'indique cet exemple :

import {Component, model, input} from '@angular/core';
@Component({...})
export class CustomCheckbox {
  // This is a model input.
  checked = model(false);

  // This is a standard input.
  disabled = input(false);
}

<!--- Parent component side --->
<custom-checkbox [checked]="true" [disabled]="false" />

Le code ci-dessus nous démontre que les deux types d'input à savoir Signal Input et Model Input permettent de faire le one-way binding c'est-à-dire dans notre cas ici la communication unidirectionnelle d'un composant parent vers un composant enfant et non l'inverse.

Cependant, le Model Input ne se limite pas à cela. Dans sa philosophie de propager de nouvelles valeurs, le Model Input est également accessible en écriture contrairement à un Signal Input. Nous pouvons donc modifier la valeur d'un Model Input depuis le composant enfant où il a été instancié ce qui n'est pas le cas du Signal Input qui est read-only. Voici une illustration :

import {Component, model, input} from '@angular/core';
@Component({
  selector: 'custom-checkbox',
  template: '<div (click)="toggle()"> ... </div>',
})
export class CustomCheckbox {
  checked = model(false);
  disabled = input(false);

  toggle() {
    // While standard inputs are read-only, you can write directly to model inputs.
    this.checked.set(!this.checked());
  }
}

Dans l'illustration de code ci-dessus, nous remarquons l'utilisation de la méthode set() sur notre Model Input. Nous pouvons en déduire que la méthode update() aussi peut être utilisée et pour conclure que notre Model Input est un WritableSignal.

Pour finir, en nous basant sur les deux extraits de code précédent, on peut facilement émettre l'hypothèse suivante :

Lorsque le composant parent ou enfant (peu importe l'un des deux) écrit une nouvelle valeur dans le Model Input, Angular peut propager cette nouvelle valeur vers le composant qui lie cette valeur.

En effet, c'est typiquement le fonctionnement du Model Input. Il ne permet pas que la liaison unidirectionnelle (one-way binding) mais également la liaison bidirectionnelle (two-way binding). Tout ceci nous fait penser à ngModel que nous utilisons déjà énormément dans Angular.

Two-way binding avec Model Input

La documentation de Angular nous présente deux situations différentes pour l'utilisation du two-way binding en se basant sur Angular Signal ou sur du JS/TS classique.

  • Two-way binding avec du JS/TS classique :
@Component({
  ...,
  // `checked` is a model input.
  // The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding
  template: '<custom-checkbox [(checked)]="isAdmin" />',
})

export class UserProfile {
  protected isAdmin = false;
}

Dans l'exemple ci-dessus, le composant CustomCheckbox peut écrire des valeurs dans checked, qui propage ensuite ces valeurs vers la propriété isAdmin dans UserProfile. Cette liaison permet de synchroniser les valeurs de checked et de isAdmin.

  • Two-way binding avec Angular Signal :
@Component({
  ...,
  // `checked` is a model input.
  // The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding
  template: '<custom-checkbox [(checked)]="isAdmin" />',
})

export class UserProfile {
  protected isAdmin = signal(false);
}

Dans cet exemple ci-dessus, le CustomCheckbox peut écrire des valeurs dans checked, qui propage ensuite ces valeurs vers isAdmin dans UserProfile. Cette liaison permet de synchroniser les valeurs de checked et de isAdmin. Cependant, il est très important de noter qu'ici, la liaison transmet le signal isAdmin lui-même, et non la valeur du signal.

Pour finir, en nous basant sur le principe du two-way binding, lorsque nous déclarons un Model Input dans un composant ou une directive, Angular crée automatiquement un event correspondant. Voici un exemple :

@Directive({...})
export class CustomCheckbox {
  // This automatically creates an output named "checkedChange".
  // Can be subscribed to using `(checkedChange)="handler()"` in the template.
  checked = model(false);
}

Angular émet cet event de changement chaque fois que nous écrivons une nouvelle valeur dans le Model Input en appelant les méthodes set() ou update().

Différences entre model() et input()

Nous avons commencé cet article en essayant de voir la similarité entre un Signal Input et un Model Input. A présent, nous verrons les éléments qui les différencient :

  • model() définit à la fois un input et un output. Le nom de l'output est toujours le nom de l'input suffixé par Change pour prendre en charge les liaisons bidirectionnelles. C'est au composant qui l'implémente de décider s'il veut utiliser seulement l'input, seulement l'output, ou les deux.

  • ModelSignal est un WritableSignal, ce qui signifie que sa valeur peut être modifiée de n'importe où à l'aide des méthodes set() et update(). Lorsqu'une nouvelle valeur est attribuée, le ModelSignal émet un output. Il en va différemment de l'InputSignal, qui est en read-only.

  • Les Model Inputs ne prennent pas en charge transform, contrairement aux Signal Inputs.

En somme, dans cet article, l'objectif a été de vulgariser le concept de Model Input. Bien qu'ayant une similarité avec le Signal Input, nous avons remarqué qu'il est beaucoup plus proche de la philosophie autour du ngModel à travers le two-way binding mais basé sur Angular Signal. Toutefois, il faut noter que Model Input est toujours Developer Preview.

Pour plus d'informations 👉 Model Inputs

Did you find this article valuable?

Support Ezéchiel Amen AGBLA by becoming a sponsor. Any amount is appreciated!