Skip to content

Angular @Input and @Output

Components rarely work in isolation, parents pass data down to children, and children notify parents of events. This lesson covers @Input()/input() and @Output()/output(), the two mechanisms Angular provides for this communication.

What Are Inputs and Outputs?

An input lets a parent component pass data into a child. An output lets a child component emit an event a parent can listen to. Together, they form the primary way components communicate in a component tree without tightly coupling them together.

Classic Angular uses the @Input() and @Output() decorators with EventEmitter. Since Angular 17.1+, the newer input() and output() functions offer a signal-friendly alternative with better type inference.

// child.component.ts
@Component({ selector: 'app-rating', standalone: true, template: `
  <button (click)="rate(1)">1</button>
  <button (click)="rate(5)">5</button>
` })
export class RatingComponent {
  @Input() max = 5;
  @Output() ratingChange = new EventEmitter<number>();

  rate(value: number) {
    this.ratingChange.emit(value);
  }
}

<!-- parent.component.html -->
<app-rating [max]="5" (ratingChange)="onRatingChange($event)"></app-rating>

The parent passes max in via property binding and listens for ratingChange via event binding.

@Input/@Output vs input()/output()

// classic decorators
@Input() value = '';
@Output() valueChange = new EventEmitter<string>();

// modern signal-based functions (Angular 17.1+)
value = input('');
valueChange = output<string>();
  • @Input() marks a class property as settable from a parent's template via property binding.
  • @Output() exposes an EventEmitter a parent can listen to via event binding.
  • input() returns a read-only signal; read its value by calling it as a function: value().
  • output() returns an emitter-like object with an .emit() method, used the same way as @Output() in templates.

Input/Output Cheatsheet

Both the classic decorator API and the modern signal-based functions.

Task Classic Modern
Declare an input @Input() name = ''; name = input('');
Required input @Input({ required: true }) id!: string; id = input.required<string>();
Read an input value this.name this.name()
Declare an output @Output() saved = new EventEmitter<void>(); saved = output<void>();
Emit an output this.saved.emit() this.saved.emit()
Bind an input (parent) [name]="value" [name]="value" (same)
Bind an output (parent) (saved)="onSaved()" (saved)="onSaved()" (same)

Required and Aliased Inputs

Inputs can be marked required, causing a compile-time error if a parent forgets to bind them, and can be aliased so the public template name differs from the internal property name.

// classic
@Input({ required: true }) userId!: string;
@Input({ alias: 'title' }) heading = '';

// modern
userId = input.required<string>();
heading = input('', { alias: 'title' });

Emitting Typed Events

Typing an EventEmitter<T> (or output<T>()) ensures parents receive strongly typed data in $event, catching mismatches at compile time rather than at runtime.

@Output() itemSelected = new EventEmitter<{ id: string; name: string }>();

selectItem(item: Item) {
  this.itemSelected.emit({ id: item.id, name: item.name });
}

Building a Two-Way Bindable Pair

Naming an input x and its matching output xChange lets consumers use Angular's "banana in a box" two-way binding syntax on your own custom component, exactly like [(ngModel)].

@Input() value = '';
@Output() valueChange = new EventEmitter<string>();

<!-- usage -->
<app-custom-input [(value)]="username"></app-custom-input>

Common Mistakes

  • Trying to mutate a parent's data directly from a child instead of emitting an output event and letting the parent decide.
  • Forgetting the Change suffix convention (xChange) when building a two-way bindable input/output pair.
  • Not typing EventEmitter<T>/output<T>(), losing type safety on the parent's $event.
  • Mixing @Input()/@Output() and input()/output() inconsistently within the same component without a clear reason.

Key Takeaways

  • Inputs (@Input()/input()) pass data from a parent down into a child component.
  • Outputs (@Output()/output()) let a child emit events a parent can listen to.
  • The modern input()/output() functions integrate naturally with Angular signals.
  • Naming an input/output pair x/xChange enables custom two-way binding syntax.

Pro Tip

Favor the modern input()/output() functions in new standalone components, they read naturally alongside signals and give better type inference for required and aliased inputs than the decorator-based API.