Angular v17.2.0 introduce una nuova api che ci consente di utilizzare il two-way binding con i Signal Inputs: Model Inputs.
Prima di procedere, ti suggerisco di leggere il mio precedente articolo “Angular Signal Inputs: verso i Signal Components”, nel quale ho approfondito i Signal Inputs e come utilizzarli.
Questo articolo è strettamente legato al precedente, ed è pensato per rispondere al punto aperto riguardo il two-way binding con i Signal Inputs.
Ora che i Model Inputs sono disponibili è tempo di approfondirne il funzionamento.
⚠️ ATTENZIONE: i Model Inputs sono ancora in developer preview ⚠️
Come creare un Model Input
Similmente ai Signal Inputs, i nuovi Model Inputs sono pensati per permetterci di sostituire i classici input creati con il decoratore @Input( ), sotto forma di Signal.
Per creare un Model Input non dobbiamo far altro che usare la funzione model( ) messa a disposizione dal pacchetto @angular/core:
import { Component, model, ModelSignal } from '@angular/core';
@Component({ ... })
export class MyComponent {
myProp: ModelSignal<string | undefined> = model<string>();
}
Code language: TypeScript (typescript)
I nostri input risulteranno di tipo ModelSignal, uno speciale tipo di Signal che Angular aggiornerà internamente ogni volta che un nuovo valore viene fornito.
La funzione update( )
L’utilizzo del two-way binding consente a due componenti padre e figlio di aggiornare il valore collegato ad un input, e di condividere infine lo stesso valore.
Per consentire l’aggiornamento da parte di un componente figlio, l’api ModelSignal ci fornisce una funzione update( ) dedicata, per aggiornare un input partendo dal suo valore corrente:
updateProp(): void {
this.myProp.update((current) => current + ' - Updated!!!');
}
Code language: TypeScript (typescript)
Utilizzare il two-way binding
Utilizzando la sintassi two-way binding per passare un valore da un componente padre ad un componente figlio, l’aggiornamento di un ModelSignal risalirà fino al componente padre:
<my-component
[(myProp)]="parentProp"
(myPropChange)="onPropChangeMethod($event)"
></my-component>
Code language: HTML, XML (xml)
Entrambi i componenti padre e figlio avranno dunque il valore dell’input sincronizzato.
Il componente padre sarà inoltre notificato tramite un output, il cui nome sarà composto a partire dal nome dell’input con l’aggiunta della parola Change
alla fine, con il nuovo valore all’interno dell’oggetto allegato all’evento.
Model Inputs con valore di default o required
Come avrete notato nei precedenti esempi, creando un Model Input è possibile fornire alla funzione model( ) un tipo come argomento per definire il tipo del valore del ModelSignal:
import { Component, model, ModelSignal } from '@angular/core';
@Component({ ... })
export class MyComponent {
myProp: ModelSignal<string | undefined> = model<string>();
}
Code language: TypeScript (typescript)
Data la natura opzionale del valore di un input, il valore di un ModelSignal viene definito come un possibile undefined.
Per liberarcene, possiamo fornire un valore di default alla funzione model( ):
myProp: ModelSignal<string> = model('default');
Code language: TypeScript (typescript)
In alternativa, possiamo definire un Model Inputs come required utilizzando la funzione model.required( ) function:
myRequiredProp: ModelSignal<string> = model.required<string>();
Code language: TypeScript (typescript)
Input options: alias e funzione transform
Come per i classici input in Angular, i Model Inputs supportano l’opzione alias:
myProp = model('default-value', { alias: 'myPropAlias' });
Code language: TypeScript (typescript)
Questa opzione ci permette di disaccoppiare il nome della variabile input del componente figlio dal nome della proprietà del DOM usata dal componente padre per passare un valore di input:
<my-component [(myPropAlias)]="parentProp"></my-component>
Code language: HTML, XML (xml)
Dov’è la funzione transform( )?
Per quanto riguarda la funzione transform( ), l’ultima opzione degli input classici, non è supportata dalla funzione model( ).
Personalmente trovo che questo abbia ha senso, immaginate soltanto i conflitti tra la natura “two-way” dei Model Inputs, e il ruolo della funzione transform( ) di manipolare il valore di un input solamente all’interno di un componente.
Fortunatamente, possiamo aggirare questa limitazione utilizzando la funzione computed( ), che ci permette di creare dati derivati a partire da uno o più Signals:
import { Component, computed, model, ModelSignal, Signal } from '@angular/core';
@Component({ ... })
export class MyComponent {
myProp: ModelSignal<string> = model('default');
myComposedProp: Signal<number> = computed(
() => this.modelOptional()?.length || 0
);
}
Code language: TypeScript (typescript)
In questo modo il valore ottenuto tramite la funzione computed( ) è inoltre confinato all’interno del componente figlio.
Grazie per aver letto questo articolo 🙏
Per quanto riguarda i Model Inputs è tutto.
Se ancora non lo hai fatto, ti consiglio di leggere il mio precedente articolo “Angular Signal Inputs: verso i Signal Components”.
Troverai una spiegazione più completa riguardo i vantaggi nell’utilizzo dei Signal Inputs e Model Inputs.
E come sempre, mi piacerebbe avere qualche feedback quindi grazie in anticipo per qualsiasi commento. 👋