Interview answer drill

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

HighIntermediateAngular
Interview focus

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
Practice more Angular interview questions
Interview quick answer

@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).

Full interview answer

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

@Input()

Parent ➜ Child

[prop]="expr"

A bindable property on the child (Angular sets it during change detection).

@Output()

Child ➜ Parent

(event)="handler($event)"

An EventEmitter the parent listens to (payload is whatever the child emits).

Mental model: Inputs are values, Outputs are events

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).

TYPESCRIPT
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);
  }
}
                  
HTML
<app-user-card
  [user]="selectedUser"
  (selected)="onUserSelected($event)"
></app-user-card>
                  
TYPESCRIPT
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.

TYPESCRIPT
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.

TYPESCRIPT
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).

TYPESCRIPT
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 @Input() object in the child

With OnPush and immutability, UI updates become unpredictable and state ownership is violated

Treat inputs as read-only; emit intent and let parent update state (new reference)

Using EventEmitter as an app-wide event bus

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 markForCheck() when needed

Wrong naming for two-way (valueChanged instead of valueChange)

[(value)] doesn’t work; parent needs manual wiring

Use the exact x + xChange naming convention

What senior candidates usually mention quickly

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.

Summary

@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).

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.