skip to content

In Angular, how does a child component emit a custom event with output() or @Output and EventEmitter, and how does the parent read its payload?

level: juniorimportance: must knowfreq 74%

answer

  1. declare, then emit
  2. OutputEmitterRef or EventEmitter
  3. parent binds with parentheses
  4. $event is the emitted value

basics

~10 s

The child declares rated = output<number>() or @Output() rated = new EventEmitter<number>() and calls rated.emit(value). The parent binds (rated)="save($event)", where $event is exactly the emitted value, not a DOM event.

solid answer

~40 s

The recommended API is `output()`: `rated = output<number>()` returns an `OutputEmitterRef<number>`, and the child calls `this.rated.emit(3)`. The older `@Output() rated = new EventEmitter<number>()` still works and is fired the same way. The parent listens with an event binding on the child's element, `(rated)="saveScore($event)"`, and `$event` is the value passed to `emit()`, so here a number, typed by the output under strict template checking. The handler runs synchronously inside `emit()`. Both APIs accept an alias for the template name, and the docs recommend camelCase names without an `on` prefix and without colliding with DOM event names. Parents cannot tell which API the child used.

code

ts · 34 lines
ts
import { Component, input, output } from '@angular/core';

@Component({
  selector: 'app-rating-stars',
  template: `
    @for (star of stars; track star) {
      <button type="button" (click)="choose(star)" [class.filled]="star <= value()">
        {{ star }}
      </button>
    }
  `,
})
export class RatingStars {
  readonly value = input(0);
  readonly rated = output<number>();
  protected readonly stars = [1, 2, 3, 4, 5];

  protected choose(star: number) {
    this.rated.emit(star);
  }
}

@Component({
  selector: 'app-product-review',
  imports: [RatingStars],
  template: `<app-rating-stars [value]="score" (rated)="saveScore($event)" />`,
})
export class ProductReview {
  score = 0;

  saveScore(stars: number) {
    this.score = stars;
  }
}

go deeper

for a junior

Declare an output with output() or @Output, fire it with emit(), and read the payload as $event in the parent's binding.

for a middle

Explain that emit() calls listeners synchronously, how aliases work, and how $event is typed under strict template checking.

for a senior

Design event names and payloads for a shared component API: camelCase, no on prefix, no DOM-name collisions, and payloads that are stable contracts.

for a principal

Decide how a large codebase moves from EventEmitter to output(), knowing parents are unaffected, and where RxJS-shaped outputs still earn their place.

## What an output is In Angular, an **output** is a custom event that a component (or directive) declares so that the component using it can react. Data flows *into* a component through inputs and *out* of it through outputs. There are two ways to declare one. **The `output()` function** from `@angular/core` is the current recommendation. It was introduced in v17.3 and marked stable in v19: ```ts import { Component, output } from '@angular/core'; @Component({ selector: 'app-rating-stars', template: '' }) export class RatingStars { readonly rated = output<number>(); // OutputEmitterRef<number> protected choose(star: number) { this.rated.emit(star); } } ``` **The `@Output()` decorator** on an `EventEmitter` field is the original API, still fully supported and very common in existing code: ```ts import { Component, EventEmitter, Output } from '@angular/core'; @Component({ selector: 'app-legacy-rating-stars', template: '' }) export class LegacyRatingStars { @Output() rated = new EventEmitter<number>(); } ``` In both cases the child fires the event by calling **`emit(value)`**. ## How the parent listens The parent binds to the output with the event-binding syntax on the child's element, exactly as it would bind to a native event: ```html <app-rating-stars [value]="score" (rated)="saveScore($event)" /> ``` - **`$event`** is the **value passed to `emit()`**, here the number of stars. It is *not* a DOM `Event` object, so there is no `$event.target`. - The handler expression runs **synchronously** inside `emit()`: by the time `emit()` returns, `saveScore` has run. - `output<void>()` declares an event with no payload, fired with `emit()`. - With strict template checking, `$event` is typed from the output's type parameter, so `saveScore(stars: number)` is checked. ## The full component The rating-stars component renders five buttons and emits the chosen value: ```ts import { Component, input, output } from '@angular/core'; @Component({ selector: 'app-rating-stars', template: ` @for (star of stars; track star) { <button type="button" (click)="choose(star)">{{ star }}</button> } `, }) export class RatingStars { readonly value = input(0); readonly rated = output<number>(); protected readonly stars = [1, 2, 3, 4, 5]; protected choose(star: number) { this.rated.emit(star); } } ``` Note that the component does **not** change `value` itself: it reports the choice, and the parent decides whether to store it and bind it back. (A component that should own a two-way value uses a model input instead, a separate subject.) ## Aliases and naming Both APIs accept an alias, the name used in templates: | API | Alias syntax | Template | |---|---|---| | `output()` | `rated = output<number>({ alias: 'ratingChosen' })` | `(ratingChosen)="..."` | | `@Output()` | `@Output('ratingChosen') rated = new EventEmitter<number>()` | `(ratingChosen)="..."` | The Angular docs' naming guidance: 1. Use **camelCase** names. 2. Do **not** prefix outputs with `on` (`onRated` reads as a handler, not an event). 3. Avoid names that **collide with DOM events** such as `click` or `change`. 4. Avoid aliases in general; they help when renaming while keeping an old public name. Like `input()`, `output()` may only be called in a **field initializer** of a component or directive; the compiler records outputs statically. ## output() versus @Output at a glance | | `output()` | `@Output()` + `EventEmitter` | |---|---|---| | Returned object | `OutputEmitterRef<T>` | `EventEmitter<T>`, an RxJS `Subject` | | Fire | `emit(value)` | `emit(value)` | | Depends on RxJS | no | yes | | Parent syntax | `(rated)="f($event)"` | same | Parents cannot tell which API a child used: the binding is identical, which is why codebases can migrate one component at a time. ## What interviewers listen for - `emit()` to fire, `(name)="handler($event)"` to listen. - `$event` is the emitted payload. - `output()` is the recommended API; `@Output` with `EventEmitter` is still supported. - Outputs are delivered to the listener bound on that element, not broadcast through the page.

  • What does $event contain when the parent binds (rated)="saveScore($event)"?
    Exactly the value the child passed to `emit()`, here a number. Angular does not wrap it in an event object, so there is no `target` or `preventDefault()`. Only bindings to native DOM events give `$event` an `Event` object.
  • How do you declare an output that carries no payload?
    Use `output<void>()` and call `emit()` with no argument; `output()` without a type argument also defaults to `void`. The parent binds `(closed)="onClosed()"` and simply ignores `$event`.
  • Is emit() synchronous?
    Yes. `OutputEmitterRef.emit()` calls every registered listener in a loop before returning, so the parent's handler has run by the time the next line in the child executes. An `EventEmitter` is also synchronous unless it was constructed with `new EventEmitter(true)`, which delivers asynchronously.

saying these in an interview costs you the question

  • $event on a component output is always a DOM Event with a target.
  • The child must call dispatchEvent to raise an output.
  • output() returns an RxJS Observable you can pipe.
  • The @Output decorator is deprecated and no longer works.
  • Output names should start with on, like onRated.