Meet Angular’s new output() API

Paul Gschwendtner
Angular Blog
Published in
4 min readMar 27, 2024

--

meet output() now in developer preview

Angular v17.3 introduces the improved API for declaring outputs as a developer preview.

The new output API provides developers with:

  • a simpler and safer API for declaring outputs in directives.
  • a consistent API aligned with the new signal inputs, queries or model functions.
  • more correct types for emitting values.
    The existing @Output API with EventEmitter does not guarantee proper type safety and could cause subtle runtime errors to surface.

The new output API is now available in developer preview and we would like your feedback.

The new API

Outputs allow component authors to emit values to parent components. We are introducing a new API to declare outputs that is closely aligned with other function-based APIs that we announced in v17, like signal inputs.

Two new functions are available for declaring outputs:

  • output() — Declares an Angular output. You can emit values by invoking the .emit function.
  • outputFromObservable() — Declares an Angular output that emits values based on a source observable.

It’s important to note that this new API is not a signal, but a more ergonomic API that reduces boilerplate code.

output() function

To get started with this new API, import the output function from @angular/core:

import {output} from `@angular/core`;

Next, declare a class field in your component and initialize it by calling the output() function. The field name will be used as the public name of the output:

import {Component, output} from '@angular/core';

@Component({…})
export class MyComp {
onNameChange = output<string>(); // OutputEmitterRef<string>
}

You can then emit new values to the output by calling the emit function on OutputEmitterRef:

class MyComp {
// …

updateName(newName: string) {
this.onNameChange.emit(newName);
}
}

Parent components can bind to the onNameChange output in their template using the event binding syntax, similar to how it works for outputs declared using the decorator-based @Output.

<!-- parent component template -->

<app-my-comp (onNameChange)="handleNameChange($event)" />

outputFromObservable() function

In addition to the new output() function, Angular provides the outputFromObservable function that integrates RxJS seamlessly.

If your application needs to expose values from RxJS streams conveniently, you can use this function to declare an Angular output that uses an observable as the source. This way, you can still take advantage of the features of observables while benefiting from the improved output API.

To get started, import the function from the @angular/core/rxjs-interop RxJS interop package, and invoke the function as the initializer of a class member. The field name will be used as the public name of the output.

import {outputFromObservable} from '@angular/core/rxjs-interop';

Then, in your component class initialize the a class field with the result of outputFromObservable as the value:

@Component({…})
export class MyComp {
onNameChange$ = new Observable<string>( … );
onNameChange = outputFromObservable(this.onNameChange$);
// ^ OutputRef<string>
}

Why is output() a better fit for your application?

In comparison to decorator-based @Output, this new API provides numerous benefits:

  • The API is conceptually aligned with the new function-based APIs for signal inputs, queries, or model().
  • We’ve simplified the API and removed complexity that isn’t relevant for outputs. For example, there is no concept of an error channel, a completion channel. Also, advanced APIs from RxJS that most developers don’t need are no longer visible.
  • Automatic clean-up of outputs upon directive destruction.
  • We’ve improved type safety for new outputs, by addressing a long-standing issue of the existing EventEmitter class. The emit function is now fully type safe and doesn’t accept values that should not be emittable.

Listening to outputs programmatically

Today in Angular, developers listen to outputs programmatically, by accessing the directive instance and invoking the .subscribe method on the output class member. We want to improve best practices around this by providing good guardrails and consistent solutions around outputs.

To begin this effort, Angular introduces a consistent API interface that all outputs are expected to implement. All new outputs, and the EventEmitter class are now implementing the OutputRef interface. An OutputRef is a reference to an Angular output that can be used to listen for new values.

export interface OutputRef<T> {
/**
* Registers a callback that is invoked whenever the output
* emits a new value of type `T`.
*
* Angular will automatically clean up the subscription when
* the directive/component of the output is destroyed.
*/
subscribe(callback: (value: T) => void): OutputRefSubscription;
}

This allows developers to continue listening to outputs consistently, regardless of whether the new output API is used, or the decorator-based @Output API.

We are also adding an additional helper to the RxJS interop package that allows listening to outputs in a RxJS-idiomatic way. This is the new outputToObservable function from @angular/core/rxjs-interop.

import {outputToObservable} from '@angular/core/rxjs-interop';

outputToObservable(this.myComp.instance.onNameChange) // Observable<string>
.pipe(…)
.subscribe(…);

Give it a try

We’re overjoyed to bring this feature to the Angular community in developer preview. The new output APIs can be used today. Developer preview allows us to make changes in response to feedback that we receive from the community.

With that in mind, we’d love your feedback as we continue to stabilize the new output APIs, or the previously announced functions for signal inputs etc.

Your feedback is valuable and we look forward to hearing from you. If you have feedback, consider letting us know in the comments, on GitHub or X.

Be sure to try it out by installing the latest version of Angular with ng update. Thanks and keep us updated.

--

--