With the release of Angular version 16, signals have made their way into the developer preview, giving us a glimpse of their true potential. However, it’s in the next major version of Angular that signal-based components will take center stage and become an integral part of Angular.
This will take signals from a generic sort of reactive primitive that can be utilised in Angular, to something that is fully integrated and a core part of the Angular experience.
This article is adapted from the following video:
Introducing Signal-Based Components
A signal based component might look something like this:
By setting the signal property to true in the decorator, Angular recognises this as a signal-based component. Essentially, this enables a new authoring experience for Angular components, whilst still keeping the existing authoring experience in place. You can mix standard components and signal based components in your codebase as you please - just switch that signals property on or off.
So, let’s talk about this new authoring experience - we are going to focus on four important points.
Fine-Grained Change Detection
Currently, Angular performs change detection by using Zone.js - this patches browser APIs so that Angular can be notified when anything that could cause a change happens. Like a promise resolving, or a setTimeout firing, or events, or HTTP requests, and so on.
Whenever anything happens that might cause a change, Angular dirty checks the entire component tree for your application to see if anything needs to be re-rendered. This isn’t as bad as it sounds, Angular is quite efficient at this and it works well. But the concept in general is a bit of a hack and it is far more efficient with signals.
In short, with signals, we get - as the cools kids say - fine grained change detection. Rather than having to check the entire application tree when anything might have changed, with signals Angular knows exactly what views will be affected by a particular signal changing, and it can perform change detection for just those views. No zone.js
required.
The change detection stuff is cool, but somewhat behind the scenes. It generally doesn’t really impact the developer experience all that much.
I have already published a video on signals and change detection if you want to know more.
Reactive Inputs with Signal Inputs
Now, let’s dive into what I think is the coolest feature of signal-based components: signal inputs. This is something Angular developers have been after in one form or another for a long time.
@Component({
selector:"app-empoloyee-list",
standalone: true,
signals: true,
template: `
<h2>Filter</h2>
<input type ="text" [(ngModel)]="searchTerm" />
<strong>Total emloyees: </strong>
<ul>
<li *ngFor="let employee of employees()">
{{ employee }}
</li>
</ul>
,
})
export class EmployeeList {
employees = input([]);
searchTerm = model("");
totalEmployees = computed(() => this.employee().length);
}
In short, signal inputs allow us to create inputs that are signals. But the key benefit of this is that we now have inputs that are reactive. The inputs are just signals, and when some new input is received, we can react to that change just like we would react to any other signal changing.
We could, for example, create a computed signal from an input signal and whenever that input signal updates, the computed signal with its derived value, will update too. This means we will no longer need to do things like having input setters that next subjects to create reactive values:
We won’t have to use ngOnChanges
to react to inputs changing - we can just use signals. In fact, the ngOnChanges
lifecycle hook will be removed from signal based components as a result of this.
Model-based Inputs
Another interesting addition to signal components, and another way to supply input to a component, is through this new concept of a model.
Just like ngModel
which you may be familiar with, it provides us with a way to achieve two way data binding.
To put this into context, let’s take a look at an example. This is a pattern I often use for a smart and dumb component set up:
We have a smart component that injects an EmployeeService
, and a dumb component whose role is to display a list of filtered employees and also provide a search box so the user can change the search term.
In this case, I want the child component to be able to update the search term, and I want the parent component to be able to react to that search term changing and update the employees input as a result.
Our parent component and child component are sharing the same writable signal. So, just like with ngModel
, if either party in this two way data binding contract updates the value they both get the value and can react to it.
Signal-based Queries
Finally, we have signal based queries, which is basically the same as the normal view queries we can use i.e. ViewChild
, ViewChildren, ContentChild, ContentChildren but now we are just given the results of those queries as a signal instead.
This will result in the ngAfterContentInit
, ngAfterViewChecked
and ngAfterContentChecked
lifecycle hooks being removed for signal components.
These features only scratch the surface of the full potential of Angular’s signal based components. For more, you can refer to the RFC document linked here.