Change Detection and Signals
One of the great things about signals is that it will reduce a great deal of the need for us to understand precisely how change detection works in Angular.
Maybe you are starting to notice a theme in this module — the older concepts that we are covering require a great deal of explanation to try and understand the mental model and how everything fits together. The newer alternatives are usually much easier to explain and are generally just more intuitive.
Before signals, there was a lot of benefit in understanding and, to an extent, “gaming” or “optimising” your application to better suit the change detection system and improve performance (or to understand why something changed but you aren’t seeing it render on screen).
I think it will still be important to understand how change detection works with signals, but it won’t be as important as it was before. There will also be much less of these strategies for improving change detection performance.
Unfortunately, although we already have signals available in Angular, the
change detection mechanisms that make full use of them are not yet stable. This
will likely be coming very soon. Our strategy throughout this course will
basically be to just use signals and approach change detection as if the new
change detection mechanisms have already been implemented. Then, when they
become available, there won’t be much if anything you will need to do to start
utilising the benefits of this approach. In the mean time, change detection will
work just fine with signals in our applications, the only downside is that —
until the change detection update is released — it will be slightly less
performant not using the OnPush
change detection strategy.
We will not be implementing the old style change detection techniques (like
using OnPush
), but as usual we will still cover the concepts as you will
likely run into Angular applications that are not using these newer approaches.
NOTE: I’ve decided to go with a “forward looking” approach for this course.
New features coming to Angular are still very much in flux and things may
change, but I believe the current path the Angular team is on will result in
OnPush
becoming obsolete when creating a signals based application. However,
it is worth noting that there are still performance benefits available
currently by setting your components to use the OnPush
change detection
strategy, because change detection in Angular is not yet fully signals based.
The rest of this lesson will be dedicated to understanding the old style of
change detection. I am using the term “old” loosely here, because most well
architected applications today use OnPush
. Unless you are exclusing working
with new Angular application code, it is still a concept you will need to
understand for a while.
As you will see, we are about to discuss a lot of ideas and nuances that need to
be kept in mind when utilising the old OnPush
change detection strategy for
optimising change detection. Whilst there is still plenty of depth in signals
change detection to uncover to deepen your knowledge of how Angular works under
the hood, the high level story of how change detection with signals works is
quite simple: if a signal is updated, the signal will handle notifying Angular
that a change has occurred, and Angular can update the specific thing that needs
updating.
As you’re about to see — this is a refreshingly simple idea. The “downside” is that we will need to use signals for any changes we want reflected in the template using this paradigm. With the old method, as complicated and ineffecient as it is, it did allow for basically any change to be reflected no matter how it was made.
Personally, I don’t see being forced to use signals as a downside as they are fantastically powerful and rather simple to use. I am going to be using signals anyway, so the change detection improvements basically come for free.
Change Detection: OnPush vs Default
To get right to the point: Angular needs to keep track of when things in your application change. Let’s say we have a class member in the class for my component:
@Component({
selector: 'app-home',
template: `
<p>{{ name }}</p>
<button (click)="changeName()">Change name</button>
`,
})
export class HomeComponent {
name = 'Josh';
changeName() {
this.name = 'Kathy';
}
}
The value for name
is initially Josh
and we are displaying that in the
template. But, we have a button that triggers the changeName
method when
clicked which will change this value to Kathy
. I encourage you to run this
example for yourself, because we are going to play with this a bit.
If you click the button, you might be unsurprised to see that the value displayed in the template changes. This is what we would want to happen, but the underlying mechanisms for Angular to achieve this seemingly simple task aren’t so simple.
We could cause a change to the template in a different way — what about
a setTimeout
that triggers the change after 2
seconds?
@Component({
selector: 'app-home',
template: ` <p>{{ name }}</p> `,
})
export class HomeComponent implements OnInit {
name = 'Josh';
ngOnInit() {
setTimeout(() => (this.name = 'Kathy'), 2000);
}
}
NOTE: We are using one of Angular’s lifecycle hooks here to run some code
when the component is initialised. There are other Angular lifecycle hooks
like OnDestroy
and AfterViewInit
as well.
Or, maybe we have a setInterval
changing the value every second:
@Component({
selector: 'app-home',
template: ` <p>{{ value }}</p> `,
})
export class HomeComponent implements OnInit {
value = 1;
ngOnInit() {
setInterval(() => this.value++, 1000);
}
}
If you run these examples, you will see that in every case the value is updated correctly. Angular detects when these changes occur and updates the template.