Yes — you mean Angular’s two-way binding convention, where @Input() + matching @Output() together behave like a model:
@Input() value
@Output() valueChange
Together → [(value)]
Full Simple Example
1. Child Component
counter.ts
import {
Component,
Input,
Output,
EventEmitter
} from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
templateUrl: './counter.html'
})
export class Counter {
@Input()
count: number = 0;
@Output()
countChange = new EventEmitter<number>();
increment() {
this.count++;
this.countChange.emit(this.count);
}
decrement() {
this.count--;
this.countChange.emit(this.count);
}
}
counter.html
<h3>Child Component</h3>
<p>Count: {{ count }}</p>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
2. Parent Component
app.ts
import { Component } from '@angular/core';
import { Counter } from './counter/counter';
@Component({
selector: 'app-root',
standalone: true,
imports: [Counter],
templateUrl: './app.html'
})
export class App {
count = 10;
}
app.html
<h2>Parent Component</h2>
<p>Parent Count: {{ count }}</p>
<app-counter [(count)]="count"></app-counter>
How [(count)] Works
This:
<app-counter [(count)]="count"></app-counter>
is shorthand for:
<app-counter
[count]="count"
(countChange)="count = $event">
</app-counter>
Angular recognizes the naming convention:
@Input()
count: number = 0;
@Output()
countChange = new EventEmitter<number>();
The output must be named <inputName>Change.
count → @Input()
countChange → @Output()
Therefore:
[(count)]
Communication Flow
Parent
count = 10
|
| [count]
↓
Child
count = 10
|
User clicks +
|
countChange.emit(11)
|
| (countChange)
↓
Parent
count = 11
So data can move in both directions:
Parent ──@Input()──→ Child
Parent ←─@Output()── Child
Modern Angular: model()
This is exactly what Angular's newer model() API simplifies.
Instead of:
@Input()
count = 0;
@Output()
countChange = new EventEmitter<number>();
you can write:
count = model(0);
Child:
import { Component, model } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
templateUrl: './counter.html'
})
export class Counter {
count = model(0);
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
}
Parent remains:
<app-counter [(count)]="count"></app-counter>
Interview Key Point
model() provides two-way binding between parent and child. It is the modern alternative to manually creating an @Input() and a corresponding <name>Change @Output().
Traditional:
@Input() count
+
@Output() countChange
↓
[(count)]
Modern:
model()
↓
[(count)]