0 day streak
javascript / expert
Snippet

Two-Way Binding Contracts with model()

The model() function creates a writable signal that automatically generates both an input and an output, enabling two-way binding syntax [(value)] without manually wiring an @Input/@Output pair. Internally Angular synthesizes a 'valueChange' output that fires whenever the signal is set, so parent components stay in sync without RxJS subjects or manual emit calls. This collapses boilerplate that previously required a getter/setter pair or an @Output EventEmitter, while keeping the component's internal state as a genuine signal usable in computed() and effect().

snippet.js
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { Component, model } from '@angular/core';
@Component({
selector: 'app-rating',
standalone: true,
template: `
<button
*ngFor="let star of stars"
(click)="setValue(star)"
[class.filled]="star <= value()"
>★</button>
`,
})
export class RatingComponent {
value = model<number>(0);
readonly stars = [1, 2, 3, 4, 5];
setValue(star: number): void {
if (star === this.value()) {
this.value.set(0);
return;
}
this.value.set(star);
}
}
// Parent usage: <app-rating [(value)]="userRating" />
angular
Breakdown
1
value = model<number>(0);
Declares a writable model signal with default 0; Angular auto-generates a paired 'value' input and 'valueChange' output.
2
this.value.set(0);
Setting the signal directly triggers the synthesized output emission, propagating the change upward to any [(value)] binding.
3
[class.filled]="star <= value()"
Reading value() in the template creates a reactive dependency, so the star highlighting updates whenever the model changes from either direction.