Angular Signal-based components tutorial

Alain Chautard
Angular Training
Published in
4 min readMar 26, 2024

--

With the release of Angular 17.3, signal-based components have become a reality. A signal-based component is one in which all data inputs, outputs, and queries are independent of RxJs and use Angular Signals instead.

In other words, here is how we used to write Angular components:

import { AsyncPipe } from '@angular/common';
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';

@Component({
//...
})
export class HelloComponent {

@Input()
name = 'World';

@Output()
greetingClicked = new EventEmitter<string>();

@ViewChild(ProfileComponent)
profileComponent: ProfileComponent;

Here is what the same component looks like with the signal-based approach:

import { AsyncPipe } from '@angular/common';
import { Component, input, output, viewChild } from '@angular/core';

@Component({
//...
})
export class HelloComponent {

name = input<string>('World');

greetingClicked = output<string>();

profileComponent = viewChild(ProfileComponent);

The main difference is that all decorators (@Input, @Output, @ViewChild, @ContentChild, @ViewChildren, @ContentChildren) can now be replaced with functions.

We also have an additional function called model(), which is both an input and an output and is perfect for two-way data bindings. You can find my short tutorial about model() here.

Why signal-based components?

In short, for better performance and change detection. When we use signal-based components, Angular knows which views (portions of component templates) depend on which signal. This means updating a signal’s value tells Angular exactly which parts of our DOM structure to update. There is no need to go through the entire component tree and check everything!

You can learn more about this with these illustrations on how change detection works.

An additional benefit is less reliance on RxJs, historically the hardest part of learning Angular. A signal-based component can be written without any subscription, subject, operator, and the like. That’s one of the next goals of the Angular team, as announced at ng-conf 2024: Delivering a future of Angular without Zone.js and RxJs.

Angular Certification Exam

How do we implement signal-based components?

In the current state of Angular, the first step to enabling signal-based components is to make your services expose signals instead of Observables and Subjects. You can use these guidelines and best practices in your services to expose your signals safely.

The second step is to replace all your decorators in your components with the new signal-based options:

  1. @Input becomes input()
  2. @ViewChild becomes viewChild(), @ContentChild becomes contentChild()
  3. @Output becomes output()

It’s worth noting that unlike input(), the output() function doesn’t return a signal. Instead, it just breaks the dependency on RxJs (Angular’s EventEmitter extends RxJs Observable) with a lightweight implementation that still relies on an .emit() method.

Assuming the following output declaration in your Typescript component code:

greetingClicked = output<string>();

Then, you would emit an output value as follows:

greetingClicked.emit('some value')

Once Rxjs code and legacy decorators are fully removed from your components, the final step is to change the component’s change detection strategy to OnPush. This will likely change in the near future, but for now, that’s the easiest way to disable the default strategy that would go through our entire component tree no matter what:

@Component({
selector: 'app-hello',
templateUrl: './hello.component.html',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelloComponent {

And that’s how you get a signal-based component with improved change detection and independence from RxJs!

You can find a full example in action here on Stackblitz.

What about RxJs?

Note that signals are designed to be interoperable with RxJs through several functions:

  1. toObservable() turns a signal into an Observable
  2. toSignal() turns an Observable into a signal
  3. outputToObservable() turns an OutputRef (the new object returned by the output() function)
  4. outputFromObservable() turns an Observable into an output.

You can keep using RxJs if you want to, especially if your services have complex operator chains. As long as you turn your result into a signal for your component, you’re good to go!

My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help web development teams learn and become comfortable with Angular.

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and the Weekly Angular Newsletter for helpful Angular tips, updates, and tutorials.

--

--