Angular has introduced a new way to define output events in components with the output() function. This addition provides a modern and concise alternative to the traditional @Output decorator.
new output() API
By replacing @Output with output(), you’ll find that your application continues to function seamlessly. This new approach maintains compatibility and offers some additional features.
1import { Component, output, OutputEmitterRef, Output } from '@angular/core';2
3@Component({4 selector: 'child-component',5 standalone: true,6 template: `<button (click)="emitClick($event)">Click here</button>`,7})8export class ChildComponent {9 @Output() deletedClick = new EventEmitter<MouseEvent>();10 deletedClick = output<MouseEvent>();11 @Output('deletedElement') elementDeleted = new EventEmitter<MouseEvent>(); //-> with alias12 elementDeleted = output<MouseEvent>({ alias: 'deletedElement' }); //-> with alias13 emitClick(event: MouseEvent): void {14 this.deletedClick.emit(event);15 this.elementDeleted.emit(event);2 collapsed lines
16 }17}1<app-component (deletedClick)="fn($event)" (deletedElement)="fn($event)" />Key Features
- Simplicity: The
output()function simplifies the syntax, making the code cleaner and easier to read. - Aliases: Similar to
@Output, you can still define aliases for your outputs. While it’s not recommended to frequently use aliases, it’s important to know that this capability is retained withoutput(). - Backward Compatibility: Switching to
output()won’t break your existing functionality. Your components will behave just as they did with@Output.
Comparing OutputEmitterRef with EventEmitter
With the introduction of output(), Angular also introduces OutputEmitterRef. Let’s compare it with the traditional EventEmitter.
1@Output() deletedClick: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();2deletedClick: OutputEmitterRef<MouseEvent> = output<MouseEvent>();EventEmitter
- Purpose:
EventEmitteris used to create events that can be emitted and subscribed to. It extendsSubjectand provides methods to emit events. - Methods:
.emit(value: T)to emit a value,.subscribe(handler: Function)to subscribe to the event.
OutputEmitterRef
- Purpose:
OutputEmitterRefis an internal type returned byoutput(), simplifying the creation and management of output events. - Methods: Similar to
EventEmitter, it includes.emit(value: T)to emit a value and.subscribe(handler: Function)to handle subscriptions.
Subscribing to Outputs Programmatically
Subscribing to outputs programmatically remains straightforward with the new output() function. Here’s how you can do it:
1import { Component, Signal, effect, viewChild, ViewChild } from '@angular/core';2import { ChildComponent } from './child-component';3
4@Component({5 selector: 'parent-component',6 standalone: true,7 imports: [ChildComponent],8 template: `<child-component />`,9})10export class ParentComponent implements AfterViewInit {11export class ParentComponent {12 @ViewChild(ChildComponent) myComponentRef!: ChildComponent;13 myComponentRef: Signal<ChildComponent> = viewChild.required(ChildComponent);14
15 constructor() {13 collapsed lines
16 effect(() => {17 this.myComponentRef().deletedClick.subscribe((event: MouseEvent) => {18 console.log('Event received:', event);19 });20 });21 }22
23 ngAfterViewInit() {24 this.myComponentRef.deletedClick.subscribe((event: MouseEvent) => {25 console.log('Event received: ', event);26 });27 }28}it automatically completes when the component or directive is destroyed.
New RxJS-Interop Functions
Two additional functions have been introduced in the RxJs Interop package to further enhance the capabilities of this new API.
outputFromObservable()
With the brand new outputFromObservable() function, you can now create an output directly from an Observable.
1import { Component, OutputRef } from '@angular/core';2import { outputFromObservable } from '@angular/core/rxjs-interop';3import { interval } from 'rxjs';4
5@Component({6 selector: 'timer-component',7 standalone: true,8 template: `...`,9})10export class TimerComponent {11 timer: OutputRef<number> = outputFromObservable(interval(1000));12 timerAlias = outputFromObservable(interval(1000), { alias: 'timerChange' });13}Both the Observable and the output are automatically completed when the component or directive is destroyed.
If an error occurs, the Observable is interrupted, causing the output to stop emitting and the error to propagate (unless it is caught).
outputToObservable()
With the brand new outputToObservable() function, you can now transform an output directly to an Observable.
1import { Component, Signal, effect, viewChild, ViewChild } from '@angular/core';2import { outputToObservable } from '@angular/core/rxjs-interop';3import { ChildComponent } from './child-component';4
5@Component({6 selector: 'parent-component',7 standalone: true,8 imports: [ChildComponent],9 template: `<child-component />`,10})11export class ParentComponent {12 myComponentRef: Signal<ChildComponent> = viewChild.required(ChildComponent);13
14 constructor() {15 effect(() => {6 collapsed lines
16 outputToObservable(this.myComponentRef().elementDeleted).subscribe((event: MouseEvent) => {17 console.log('Observable:', event);18 });19 });20 }21}Again, both the Observable and the output are automatically completed when the component or directive is destroyed.
Furthermore, due to the absence of errors in the outputs, the resulting Observable never emits error notifications.
Acknowledgment
This post draws inspiration and references from source post, providing valuable insights into Angular’s new output() function and related concepts. Special thanks to the original author for their contributions to the Angular community.
Source Code
You can find the source code for this post on GitHub, which includes examples and additional details discussed here.
Conclusion
The introduction of the output() function, OutputEmitterRef, and new RxJS interop functions in Angular provides a more streamlined way to handle output events and integrate reactive programming.