An Introduction to Signals
Signals are one of the new features in Angular, and it is pretty safe to say they are the new feature.
It might be hard to see why… or even understand what exactly it is they allow us to do.
For example, here is a basic interpolation that we have already covered:
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
template: `<p>Hi, {{ name }}</p>`,
})
export class HomeComponent {
name = 'Josh';
}
Now here is what it will look like with signals:
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-home',
template: `<p>Hi, {{ name() }}</p>`,
})
export class HomeComponent {
name = signal('Josh');
}
NOTE: All of the signal functions you see in this lesson like signal
,
computed
, and effect
can all be imported from @angular/core
It’s basically exactly the same… except we have to wrap our Josh
value with
this signal
function that is imported from @angular/core
, and in the
template we also have to use this funny syntax to access the value:
{{ name() }}
When we create our name
:
name = signal('Josh');
It will become whatever this signal
function we are calling returns. The
signal
function will actually return us something called a WritableSignal
which is a type of signal (specifically, one you are allowed to update).
So, basically, the signal function will return us a signal. That means name
is
a signal.
To access the value of a signal
we call it as a function — that is what we are
doing in the template:
{{ name() }}
If we did this instead:
{{ name }}
You would see some nonsense like this in the template:
Hi, function signalFn() { producerAccessed(node); return node.value; }
So it seems that signals do basically the same thing as declaring a class member and using an interpolation, except that it requires extra syntax and comes with some extra confusing nonsense.
Signals and Change Detection
Ok, so given how excited everyone is about these things, it’s safe to say there is some benefit to them.
Perhaps the key thing is how they tie into change detection. This is a topic we are going to cover in a lot more depth in a few lessons time, but what is important to understand for now is that Angular needs to know when something has changed in order to update the view and actually display the change to the user.
For example:
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
template: `<p>Hi, {{ name }}</p>`,
})
export class HomeComponent {
name = 'Josh';
}
If name
changes from Josh
to Kathy
for any reason, Angular needs to know
so that it can update the DOM appropriately. How does Angular keep track of when
things change? To put it simply, it use a mechanism (Zone.js) that will notify
it when anything possibly could have happened to cause a change. Things that
could potentially cause a change are things like setTimeout
or setInterval
or a promise resolving, or an HTTP request completing, and so on.
Angular gets notified that there could have been a change, and then it checks every component to see if anything needs to be updated.
This isn’t as bad as it sounds — Angular can actually do this quite fast and there are ways to optimise it on the developers end. But, there is certainly room for improvement.
That is where signals come in. It flips this whole problem on its head. Instead of Angular checking whenever anything could have possibly changed, it becomes the role of the signal to tell Angular when it has changed.
If we use signals for our values that change, then it allows Angular to do a lot less change detection work. So, if we do something like this:
name = signal('Josh');
updateName(name: string) {
this.name.set(name);
}
We are calling set
on our signal which will update its value, but it can also
notify Angular that it has changed. It’s also not just Angular that it will
notify about the change, we can make use of this mechanism as well (more on that
later).