Skip to content

@Input and @Output

Components that live in isolation are not very useful. Real applications require components to communicate — a parent passes data to a child, and a child notifies the parent when something happens. Angular provides two decorators for this: @Input() and @Output().

@Input() marks a property as something a parent component can set. It is how data flows from parent to child.

In CinemaVault, MovieCard needs to display a movie. It receives the movie from its parent:

movie-card.ts
import { Component, Input } from '@angular/core';
import { Movie } from '../../models/movie.model';
@Component({
selector: 'app-movie-card',
templateUrl: './movie-card.html',
styleUrl: './movie-card.css'
})
export class MovieCard {
@Input({ required: true }) movie!: Movie;
}

The required: true option tells Angular to throw an error if the parent does not provide this input — helpful for catching mistakes during development.

The parent passes the movie using property binding:

home.html
@for (movie of movies; track movie.id) {
<app-movie-card [movie]="movie" />
}

The [movie]="movie" syntax is property binding — it sets the child’s movie input to the parent’s movie variable.

@Output() marks an EventEmitter property as something the child can emit. Parents listen by binding to the output with (eventName) syntax.

Say MovieCard should notify its parent when the user clicks the watchlist toggle:

movie-card.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Movie } from '../../models/movie.model';
@Component({
selector: 'app-movie-card',
templateUrl: './movie-card.html',
styleUrl: './movie-card.css'
})
export class MovieCard {
@Input({ required: true }) movie!: Movie;
@Output() watchlistToggled = new EventEmitter<Movie>();
toggle(): void {
this.watchlistToggled.emit(this.movie);
}
}
movie-card.html
<div class="card">
<img [src]="posterUrl" [alt]="movie.title" />
<h3>{{ movie.title }}</h3>
<button (click)="toggle()">Add to Watchlist</button>
</div>

The parent listens with event binding:

home.html
@for (movie of movies; track movie.id) {
<app-movie-card
[movie]="movie"
(watchlistToggled)="onWatchlistToggle($event)"
/>
}
home.ts
onWatchlistToggle(movie: Movie): void {
this.watchlistService.toggle(movie);
}

The $event variable in the template contains whatever value was passed to emit() — in this case, the Movie object.

Home
↓ [movie]="movie" (input: data flows down)
MovieCard
↑ (watchlistToggled) (output: events flow up)
Home

This unidirectional data flow makes it easy to reason about where data comes from and where it goes.

  1. In your practice project, generate a component components/movie-card.
  2. Add an @Input({ required: true }) title!: string property.
  3. Display the title in the template: <h3>{{ title }}</h3>
  4. In app.component.html, use <app-movie-card [title]="'Inception'" />.
  5. Add an @Output() cardClicked = new EventEmitter<string>() and a method that emits the title on button click.
  6. In app.component.html, listen: (cardClicked)="onCardClicked($event)" and log the received value.
  • @Input() marks a property that a parent component can set via property binding [property]="value".
  • { required: true } makes an input mandatory — Angular throws at runtime if it is missing.
  • @Output() marks an EventEmitter that a parent can listen to via event binding (eventName)="handler($event)".
  • The child calls this.myOutput.emit(value) to fire the event; the parent receives value as $event.
  • Data flows down via inputs; events flow up via outputs.