@Input() defines an API for data flowing from parent → child via property binding. @Output() defines an API for events flowing from child → parent via EventEmitter (or newer output helpers). Interviewers expect you to explain timing (updates over time), typing, OnPush/immutability implications, and when NOT to use EventEmitter (cross-component event bus).
How do @Input() and @Output() work in Angular, and what pitfalls do seniors mention in interviews?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea@Input() is the child component’s public data API. The parent binds values into it: parent ➜ child.@Output() is the child component’s public event API. The child emits typed events and the parent listens: child ➜ parent.
Decorator | Direction | Template syntax | What it really is |
|---|---|---|---|
| Parent ➜ Child |
| A bindable property on the child (Angular sets it during change detection). |
| Child ➜ Parent |
| An EventEmitter the parent listens to (payload is whatever the child emits). |
Typical pattern: pass data down, emit intent up
Child receives the current value via @Input and emits user intent via @Output (don’t mutate parent state directly).
import { Component, EventEmitter, Input, Output, ChangeDetectionStrategy } from '@angular/core';
type User = { id: string; name: string; email: string };
@Component({
selector: 'app-user-card',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<article>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button type="button" (click)="select()">Select</button>
</article>
`
})
export class UserCardComponent {
@Input({ required: true }) user!: User;
@Output() readonly selected = new EventEmitter<string>();
select(): void {
this.selected.emit(this.user.id);
}
}
<app-user-card
[user]="selectedUser"
(selected)="onUserSelected($event)"
></app-user-card>
onUserSelected(userId: string): void {
// parent owns the state update
console.log('Selected:', userId);
}
Inputs change over time
Interviewers often ask how you react when an input changes after the first render. The correct answers are: ngOnChanges, an @Input setter, or (in newer Angular) signals-based inputs.
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
type User = { id: string; name: string };
@Component({
selector: 'app-user-badge',
template: `{{ display }}`
})
export class UserBadgeComponent implements OnChanges {
@Input() user?: User;
display = '—';
ngOnChanges(changes: SimpleChanges): void {
if (changes['user']) {
this.display = this.user ? `${this.user.name} (${this.user.id})` : '—';
}
}
}
Custom two-way binding (banana-in-a-box)
If you name an output xChange next to an input x, Angular enables [(x)] syntax. This is a common interview topic (and a common place to make naming mistakes).
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<button type="button" (click)="dec()">-</button>
<span>{{ value }}</span>
<button type="button" (click)="inc()">+</button>
`
})
export class CounterComponent {
@Input() value = 0;
@Output() readonly valueChange = new EventEmitter<number>();
inc(): void { this.valueChange.emit(this.value + 1); }
dec(): void { this.valueChange.emit(this.value - 1); }
}
// parent template:
// <app-counter [(value)]="count"></app-counter>
Pitfall (interview-grade) | What goes wrong | Fix |
|---|---|---|
Mutating an | With | Treat inputs as read-only; emit intent and let parent update state (new reference) |
Using | Tight coupling + unclear ownership + lifecycle surprises | Use a service with RxJS (Subject/Observable) or a store for cross-component events |
Heavy logic in template bindings/handlers | Hard to test, noisy templates, can run often | Keep template thin; call a method and do logic in TS |
Unstable identity with OnPush (mutating arrays/objects in place) | Child doesn’t re-render because reference didn’t change | Prefer immutable updates (new array/object) or explicit |
Wrong naming for two-way ( |
| Use the exact |
@Input defines the child’s data API (parent ➜ child). @Output defines the child’s event API (child ➜ parent). In interviews, emphasize: typed payloads, reacting to input changes correctly, OnPush + immutability behavior, and using EventEmitter only for component outputs (not as a global event bus).