• Skip to primary navigation
  • Skip to main content
  • Skip to footer

Codemotion Magazine

We code the future. Together

  • Discover
    • Events
    • Community
    • Partners
    • Become a partner
    • Hackathons
  • Magazine
    • Backend
    • Frontend
    • AI/ML
    • DevOps
    • Dev Life
    • Soft Skills
    • Infographics
  • Talent
    • Discover Talent
    • Jobs
    • Manifesto
  • Companies
  • For Business
    • EN
    • IT
    • ES
  • Sign in

Davide PassafaroApril 3, 2024

Angular new output() function

Languages and frameworks
Scopriamo la nuova funzione (output) di Angular.
facebooktwitterlinkedinreddit

During the latest months, Angular v17 has introduced a set of new function-based APIs focused on enhancing Signal integration within components and directives. This includes Signal Inputs, Model Inputs and Signal Queries.

Considering the full picture, we are only missing one core API to conclude this journey, and this article is dedicated to precisely that: Angular v17.3.0 latest addition, the new output() function.

Recommended article
February 6, 2025

Top Programming Languages in 2025

Lucilla Tomassi

Lucilla Tomassi

Languages and frameworks

The new output( ) API

Similar to the function-based APIs introduced so far, you now have a brand-new output() function designed to replace the @Output decorator.

To declare an output, you can now use the output() function when we declare a property of a component or directive:

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

@Component({
  selector: 'my-component',
  standalone: true,
  template: `<button (click)="emitClick($event)">Click here</button>`,
})
export class MyComponent {
  buttonClick = output<MouseEvent>();
  alias = output<MouseEvent>({ alias: 'aliasClick' });

  emitClick(event: MouseEvent): void {
    this.buttonClick.emit(event);
    this.alias.emit(event);
  }
}
Code language: TypeScript (typescript)

As you can see from the example, this API also supports the alias property and exposes the emit() function.

Furthermore, you can listen for the output event in the parent components, using Angular event binding syntax in the template:

<my-component
  (buttonClick)="myFunction($event)"
  (aliasClick)="myFunction($event)"
/>
Code language: HTML, XML (xml)

So far, everything works almost identically to @Output decorator-based outputs.

Let’s now move on to the subscribe() function, which brings some changes.

How to subscribe to outputs programmatically

Using the output() function you get an instance of type OutputEmitterRef:

buttonClick: OutputEmitterRef<MouseEvent> = output<MouseEvent>();
Code language: TypeScript (typescript)

In addition to the already-mentioned emit() method, which has remained unchanged on the surface, this class also exposes a subscribe() method to listen to the event programmatically:

import { Component, Signal, effect, viewChild } from '@angular/core';
import { MyComponent } from './my-component';

@Component({
  selector: 'my-parent-component',
  standalone: true,
  imports: [MyComponent],
  template: `<my-component />`,
})
export class MyParentComponent {
  myComponentRef: Signal<MyComponent> = viewChild.required(MyComponent);

  constructor() {
    effect(() => {
      this.myComponentRef().myOutput.subscribe((event: MouseEvent) => {
        console.log('Manual subscription:', event);
      });
    });
  }
}
Code language: TypeScript (typescript)

The subscription generated by this class is not based on RxJs, so you can’t use the pipe() function and operators. Nevertheless, it still exposes an unsubscribe() function to terminate it programmatically:

const subscription = this.myComponentRef().myOutput.subscribe(
 (event: MouseEvent) => {
   // Unsubscribes to listen only the first event, if any
   subscription.unsubscribe();
  }
);
Code language: TypeScript (typescript)

It is also automatically completed when the component, or directive, is destroyed.


New rxjs-interop functions

To further enhance the capabilities of this new API, two additional functions have been introduced in the RxJs Interop package.

outputFromObservable( )

Thanks to the brand-new outputFromObservable() function, you can now create an output starting from an Observable.

The generated output emits each new value emitted by the Observable:

import { Component, OutputRef } from '@angular/core';
import { outputFromObservable } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';

@Component({
  selector: 'my-timer-component',
  standalone: true,
  template: `...`,
})
export class MyTimerComponent {
 timer: OutputRef<number> = outputFromObservable(interval(1000));
 timerAlias = outputFromObservable(interval(1000), { alias: 'timerChange' });
}
Code language: TypeScript (typescript)

Both the Observable and the output are automatically completed when the component, or directive, is destroyed.

If an error occurs, the Observable is interrupted, consequently the output stops emitting and the error propagates (if not caught).

outputToObservable( )

Thanks to the new outputToObservable() function we can instead transform an output into an Observable:

import { Component, Signal, effect, viewChild } from '@angular/core';
import { outputToObservable } from '@angular/core/rxjs-interop';
import { MyComponent } from './my-component';

@Component({
  selector: 'my-parent-component',
  standalone: true,
  imports: [MyComponent],
  template: `<my-component />`,
})
export class MyParentComponent {
  myComponentRef: Signal<MyComponent> = viewChild.required(MyComponent);

  constructor() {
    effect(() => {
      outputToObservable(this.myComponentRef().myOutput)
        .subscribe((event: MouseEvent) => {
          console.log('Manual subscription:', event);
        });
    });
  }
}
Code language: TypeScript (typescript)

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.


Thanks for reading so far 🙏

I’d like to know your feedback so please feel free to contact me for any. 👋

Then, if you really liked it, share it among your community, tech bros, and whoever you want. 👋😁

Related Posts

perché esplorare nuovi framework potrebbe far fallire il tuo progetto

Adopting New Frameworks Could Jeopardize Your Project

Matteo Baccan
September 2, 2024
gazelle logo, a new framework for flutter and dart.

Gazelle: A Flexible Framework for Building Custom and Scalable Backends in Dart

Codemotion
August 7, 2024
frameworks

Unpopular Opinion: Framed by the Framework

Arnaldo Morena
June 4, 2024
nuxt.js guide with code examples

In-Depth Guide to Nuxt.js with Code Examples

Codemotion
May 30, 2024
Share on:facebooktwitterlinkedinreddit
Davide Passafaro
My name is Davide Passafaro and I am Senior Frontend Engineer at Awork. I lead two developer communities in Rome, GDG Roma Città and Angular Rome, and actively contribute to the tech community as a writer and speaker. When i shut down my computer I like to play board games and archery, not both together till now. I also like escape rooms and memes.
OramaSearch: Your New Favorite Search Engine
Previous Post
Python Environment Variables: 4 Pitfalls and How to Avoid Them
Next Post

Footer

Discover

  • Events
  • Community
  • Partners
  • Become a partner
  • Hackathons

Magazine

  • Tech articles

Talent

  • Discover talent
  • Jobs

Companies

  • Discover companies

For Business

  • Codemotion for companies

About

  • About us
  • Become a contributor
  • Work with us
  • Contact us

Follow Us

© Copyright Codemotion srl Via Marsala, 29/H, 00185 Roma P.IVA 12392791005 | Privacy policy | Terms and conditions