@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).
Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
How do @Input() and @Output() work in Angular, and what pitfalls do seniors mention in interviews?Frontend interview answer
This Angular interview question tests whether you can explain @Input and @Output work in Angular, connect it to production trade-offs, and handle common follow-up questions.
- @Input and @Output work in Angular explanation without falling back to memorized docs wording
- Components and Inputs reasoning, edge cases, and production failure modes
- How you would answer the most likely Angular interview follow-up
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);
}
Integrated example: input immutability + typed output event
A strong production answer shows both contracts at once: the child renders parent-owned input data, but emits a typed event payload instead of mutating that input directly.
type RenameUserEvent = { id: string; nextName: string };
@Component({
selector: 'app-user-editor',
template: `
<input [value]="user.name" (input)="rename(($event.target as HTMLInputElement).value)" />
<button type="button" (click)="save()">Save</button>
`
})
export class UserEditorComponent {
@Input({ required: true }) user!: User;
@Output() readonly renameRequested = new EventEmitter<RenameUserEvent>();
private draftName = '';
rename(nextName: string): void {
this.draftName = nextName;
}
save(): void {
this.renameRequested.emit({ id: this.user.id, nextName: this.draftName || this.user.name });
}
}
// parent template
// <app-user-editor [user]="user" (renameRequested)="renameUser($event)"></app-user-editor>
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 |
Output events do not bubble like DOM events
An @Output() only notifies the parent that binds to that component event. Wrapping a native button does not magically forward the browser click up the component tree. If grandparents need the event, each component boundary must re-emit or the state flow should move to a service/store.
@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).
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.