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?
answer
- declare, then emit
- OutputEmitterRef or EventEmitter
- parent binds with parentheses
- $event is the emitted value
basics
~10 sThe 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 sThe 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 linesimport { 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
Declare an output with output() or @Output, fire it with emit(), and read the payload as $event in the parent's binding.
Explain that emit() calls listeners synchronously, how aliases work, and how $event is typed under strict template checking.
Design event names and payloads for a shared component API: camelCase, no on prefix, no DOM-name collisions, and payloads that are stable contracts.
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.