model() creează un semnal care e în același timp input și output. Componenta îl poate scrie (set/update), iar scrierea ajunge automat la părinte prin evenimentul numeChange. Asta e baza sintaxei two-way [( )] („banana in a box”).
Exemplu
import { Component, input, model } from '@angular/core';
@Component({
selector: 'app-toggle',
template: `
<button role="switch" [attr.aria-checked]="checked()" (click)="checked.set(!checked())">
<span class="knob"></span>
</button>
{{ label() }}
`,
})
export class Toggle {
// model() = input + output „checkedChange”, într-un singur semnal scriibil
readonly checked = model(false);
readonly label = input('');
}{
"notifications": false,
"darkMode": true,
"guests": 2,
"plan": "Free"
}model.required
@Component({ selector: 'app-stepper', /* ... */ })
export class Stepper {
readonly value = model.required<number>(); // părintele TREBUIE să dea o valoare
readonly min = input(0);
readonly max = input(10);
increment() {
this.value.update((v) => Math.min(v + 1, this.max())); // scrie → notifică părintele
}
}model() sau input + output?
// model(): starea „aparține” amândurora — controlul o modifică, părintele o poate seta
readonly selected = model<string>();
// input + output: copilul doar PROPUNE, părintele decide (poate refuza / valida / salva pe server)
readonly value = input.required<number>();
readonly valueChange = output<number>();
// părinte: (valueChange)="confirmAndSave($event)"- Controale de tip „valoare”: toggle, slider, stepper, color picker, date picker.
- Stare de UI pe care părintele poate vrea s-o controleze: tab activ, panou expandat, dialog deschis.
- Echivalentul simplu al lui
ngModelpentru componente proprii.
- Părintele trebuie să valideze sau confirme schimbarea (ex. salvare pe server).
- Evenimente care nu sunt „valoarea mea s-a schimbat”:
(save),(delete). - Câmpuri dintr-un formular Signal Forms → implementează
FormValueControl(vezi Controale custom) — tot cumodel(), dar integrat cu validarea.
Ce generează model() de fapt
Un câmp checked = model(false) declară două lucruri pe componentă: un input numit checked și un output numit checkedChange. Sintaxa [(checked)] e doar o prescurtare pentru amândouă. De aceea părintele poate alege: two-way, doar input sau doar output.
export class Toggle {
readonly checked = model(false);
// Angular vede pe componentă:
// input: checked
// output: checkedChange
}
<!-- aceleași două legături, scrise în patru feluri -->
<app-toggle [(checked)]="enabled" />
<app-toggle [checked]="enabled()" (checkedChange)="enabled.set($event)" />
<app-toggle [checked]="true" /> <!-- doar input -->
<app-toggle (checkedChange)="log($event)" /> <!-- doar output -->Un detaliu care surprinde: dacă părintele leagă o valoare fixă ([checked]="true"), copilul poate totuși să-și schimbe valoarea cu set(). Schimbarea rămâne locală până când părintele trimite o valoare nouă, care o suprascrie. Dacă vrei ca părintele să fie singura sursă de adevăr, leagă un semnal cu [( )].
Obiecte și liste în model()
Ca orice semnal, model() anunță o schimbare doar când primește o referință nouă. Dacă modifici un câmp din obiect pe loc, nici părintele, nici template-ul nu află. Scrie mereu o copie, cu update().
export class AddressEditor {
readonly address = model<Address>({ street: '', city: '' });
setCity(city: string) {
// ✗ mutație: nimeni nu află, părintele rămâne cu valoarea veche
this.address().city = city;
// ✓ referință nouă: template-ul și părintele se actualizează
this.address.update((a) => ({ ...a, city }));
}
}Opțiunile lui model()
model() are doar două opțiuni: alias (numele public, iar output-ul devine aliasChange) și debugName (pentru Angular DevTools). Nu are transform ca input(). Dacă ai nevoie să transformi valoarea primită, de exemplu un string din HTML în număr, folosește input() cu transform plus un output() separat.
Capcane frecvente
[(checked)]="notifications()"— cu paranteze — nu e two-way: legi o valoare, nu semnalul. Scrie[(checked)]="notifications".- Semnal read-only la părinte (un
computed) în[( )]→ eroare de compilare. Two-way are nevoie de unWritableSignalsau o proprietate simplă. - Doi „stăpâni” ai stării: dacă și părintele și copilul fac logică pe aceeași valoare, devine greu de urmărit. Stabilește cine e sursa de adevăr.
Fișă rapidă
model<T>() // ModelSignal<T | undefined>
model(default) // ModelSignal<T>
model.required<T>()
m.set(v); m.update(fn); // scrie local + emite `${nume}Change`
<app-x [(value)]="sig" /> // two-way pe semnal
<app-x [value]="v" (valueChange)="onChange($event)" />