Change detection is important in Angular, ensuring that the UI shows the application's current state. Traditionally, Angular uses Zone.js
To automatically detect and apply these changes to the UI, but with the Angular 18 release, a new approach was introduced—zoneless change detection, as this article will show.
Session Replay for Developers
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay suite for developers. It can be self-hosted in minutes, giving you complete control over your customer data.
Happy debugging! Try using OpenReplay today.
Change detection in Angular involves tracking and updating changes in data components to the UI. It ensures that interaction is displayed in real-time when we interact with an Angular app. This article will take us through change detection, how Zone.js
works, and the reason for the shift to applications without zones. We will also see how to set up a change detection process in a Zoneless application.
Zone.js
works by monkey-patching all async
APIs and creating a zone for each async
task. It will keep track of operations from their beginning to their end. The change detection mechanism is triggered when the task is done, and the UI is updated.
It's important to note that Angular will not discontinue Zone.js
since many apps have already been made using it. They will continue to support Zone.js
by making critical fixes and security patches.
Why is Angular moving towards Zoneless?
As applications scale, the hands-off reactivity used by Zone.js
begins to show some weaknesses. Some of these challenges are:
Maintenance and performance safeguarding: Maintaining reactivity becomes more challenging as the application grows. This is because
Zone.js
relies on DOM events andasync
tasks to detect changes in the application state, but it doesn't know whether the state really changed. This causes it to trigger synchronization more often than it is supposed to.Debugging reactivity: It is difficult to identify the root cause of reactivity problems. With
Zone.js
, it is challenging to know if the code breaks because it is outside the zone. This is a result ofZone.js
’s challenging stack trace interpretation.Cost of Zones with new Web APIs: Application loading cost and start-up time increase when new Web APIs are added. This will increase the application bundle size. Removing this dependency makes the application more efficient.
Introduction to Zoneless Applications
Zoneless applications do not use Zone.js
for change detection. Rather, the component itself notifies Angular's change detection mechanism to update the UI after the completion of an async
function.
It gives developers additional flexibility over the timing and mechanism of change detection, improving the application's performance and efficiency.
We will demonstrate with some examples without Zone.js
that require change detection. To follow up on this tutorial, first install Angular 18 by running the command below.
npm install -g @angular/cli
Run the command below in the command line to create a new Angular project.
ng new <filename>
Open the file in your code editor. After that, disable Zone.js
. We can do this by removing Zone.js
from the polyfills
in the angular.json
file.
"polyfills": [
"zone.js" // remove zone.js
]
Then in the app.config.ts
file, add provideExperimentalZonelessChangeDetection()
in the providers
array. After you've done it, you have successfully configured your Angular app to use the Zoneless feature for change detection instead of Zone.js
.
import { ApplicationConfig } from '@angular/core';
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideRouter(routes),
],
};
Setting output in the component
After configuring our application to be Zoneless, we will test the change detection mechanism by defining a variable and updating its state.
In the code below, we update the PageNumber
to 2 using the ngOnInit method. Angular's change detection mechanism detects this change and automatically updates the UI.
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CommonModule],
template: `
<div>Page Number: {{ PageNumber }}</div>
`,
})
export class AppComponent implements OnInit {
title = 'zoneless';
PageNumber = 0;
ngOnInit(): void {
this.PageNumber += 2;
}
}
In the image below, the PageNumber
variable is automatically updated from 0 to 2.
Timer event change
Timer event change in Zoneless isn't automatically updated. It works like with OnPush as you'll need to manually trigger it because there's no automatic change detection as in Zone.js
.
To test this, let's create a timer event using setTimeout()
to update the PageNumber
to 2 after 1 second.
@Component({
selector: 'app-root',
template: `
<div>Page Number: {{ PageNumber }}</div>
`,
})
export class AppComponent implements OnInit {
title = 'zoneless';
PageNumber = 0;
ngOnInit(): void {
setTimeout(() => {
this.PageNumber += 2;
}, 1000);
}
}
The image shows that the PageNumber
variable is not set to 2 after 1 second.
This shows that Angular might not automatically detect asynchronous operations. To initialize it manually, ChangeDetectorRef can be applied to trigger the event handler.
To do this, inject ChangeDetectorRef
into the AppComponent
and use the changeDetectorRef.detectChanges()
function to trigger the update.
import { ChangeDetectorRef, inject } from '@angular/core';
export class AppComponent implements OnInit {
title = 'zoneless';
changeDetectorRef = inject(ChangeDetectorRef);
PageNumber = 0;
ngOnInit(): void {
setTimeout(() => {
this.PageNumber += 2;
this.changeDetectorRef.detectChanges();
}, 1000);
}
}
The image shows that the PageNumber
variable is updated to 2 after 1 second.
Signal Update
In Zoneless applications, the signal API automatically updates the reactive state to the UI, just as it does in Zone.js
.
To test this, let's create a signal
and initialize it with the value 5. Then update this value by calling the this.signal.set()
after 1-second delay.
import { signal } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div>Signal: {{ signal() }}</div>
`,
})
export class AppComponent implements OnInit {
title = 'zoneless';
signal = signal(5);
ngOnInit(): void {
setTimeout(() => {
this.signal.set(2);
}, 1000);
}
}
The image shows that the signal
is updated from 5 to 2 in the Zoneless application.
Event Change
The Angular detection mechanism detects when a user interacts with elements, in this case below a button, and triggers an update.
Let's create a property eventChange
and set it to 0. In the template, we will define a button that, on clicking, will change the value of the event to 200.
@Component({
selector: 'app-root',
template: `
<button (click)="eventChange = 200">Click here</button>
{{ eventChange }}
`,
})
export class AppComponent {
title = 'zoneless';
eventChange = 0;
}
In the image below, once the button is clicked the eventChange
variable is automatically updated to 200.
Async pipe
The async
pipe subscribes to an Observable
and returns the latest value emitted. When this new value is emitted, the async
pipe automatically updates the UI after triggering the change detection.
In the code below, we create an Observable
time$
that emits a value every second. The async
pipe updates the displayed value, which subscribes to time$
.
import { interval } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<div>Current Time: {{ time$ | async }}</div>
`,
})
export class AppComponent {
time$ = interval(1000); // Emits values every second
}
The image below shows the value of time$
, updated every second.
It is vital to note that Zoneless change detection is still an experimental feature and major updates could be added to its API.
Conclusion
Angular moving towards Zoneless change detection will address some challenges with Zone.js
such as maintenance, performance, and debugging, thereby making Angular applications perform more efficiently. We looked through examples where we needed to manually trigger updates using ChangeDetectorRef
, and leverage async pipe
for automatic updates. Zoneless change detection is still experimental but offers a promising alternative for building more scalable applications.
Top comments (0)