Get ready to go Zoneless
angularday 2024 | Verona | November 8
�
�
let count = 1;
count = 5;
let count = 1;
⚡️
⚡️
⚡️
⚡️
⚡️
@Component({
selector: "my-component",
template: `
<div>{{ counter }}</div>
<button (click)="increase()"></button>
`,
})
export class MyComponent {
count = 0;
increase(): void {
this.count++;
}
}
@Component({
selector: "my-component",
template: `
<div>{{ counter }}</div>
<button (click)="increase()"></button>
`,
})
export class MyComponent {
count = 0;
increase(): void {
setInterval(() => {
this.count++;
}, 1000);
}
}
Zone.js
Z
Powered by
Monkey patching is a technique to modify or extend existing code
at runtime without altering the original source code
This triggers Angular change detection
Zone.js
Z
const originalSetTimeout = window.setTimeout;
window.setTimeout = function (fn, ms) {
originalSetTimeout(fn, ms);
this.applicationRef._tick();
};
originalSetTimeout(..., ...);
MacroTask
MicroTask
EventTask
Zone.js
Z
AppComponent
ApplicationRef
NgZone
AppComponent
ApplicationRef
ApplicationRef
AppComponent
Event
<img
[alt]="hero.name"
[src]="hero.imageUrl"
/>
<app-hero-detail
[hero]="hero"
/>
<main>
<h2>Hello {{ firstName }}!</h2>
<input type="text" [(ngModel)]="firstName" />
</main>
@if(show){
@for (item of list; track item.id) {
<p>{{ item.name }}</p>
<img [src]="item.image" />
}
}
Angular default change detection
NgZone
OnPush
AppComponent
ApplicationRef
ApplicationRef
AppComponent
Event
OnPush
NgZone
OnPush
AppComponent
ApplicationRef
ApplicationRef
Event
OnPush
OnPush
AppComponent
AppComponent
Let's migrate every component to
OnPush startegyyy
@Component({
selector: 'my-component',
template: `
<button (click)="addItem()">Add</button>
<my-child [items]="items" />
`
})
export class MyComponent {
items = [];
addItem() {
this.items.push(42);
this.items = [...this.items, 42];
}
}
@Component({
selector: 'my-component',
template: `
<button (click)="loadData()">Load</button>
<my-child [items]="items" />
`
})
export class MyComponent {
cdr = inject(ChangeDetectorRef);
myService = inject(MyService);
items = [];
loadData() {
this.myService.getData().subscribe(data => {
this.items = data;
this.cdr.markForCheck();
})
}
}
Spread operators and markForCheck( )
EVERYWHERE
Default
OnPush
Checks all components binded values on every
event or model change
Skip checks unless
Performance
DevEx
const name = signal('Mario');
const lastName = signal('Rossi');
firstName.set('Giorgio');
firstName.set('Matteo');
const fullName = computed(() => `${name()} ${lastName()}`);
effect(() => {
console.log(`Full Name: ${fullName()}`);
});
Angular templates can now track
Signals updates using effect( )
<div>
{{ description() }}
</div>
<app-hero-detail
[hero]="hero()"
/>
@if(show()){
@for (item of list(); track item) {
<p>{{ item.name }}</p>
<img [src]="item.image" />
}
}
NgZone
OnPush
Signal update
AppComponent
ApplicationRef
ApplicationRef
OnPush
OnPush
OnPush
AppComponent
AppComponent
OnPush
Global Mode
Targeted Mode
This is great...
BUT
Zone.js
Z
Zone on, Zone off
Circa 15 minuti
fino a qui
NgZone
ApplicationRef
AppComponent
AppComponent
ApplicationRef
ChangeDetectionScheduler
Without Zone.js, Angular can only rely on its own APIs
These APIs include:
ChangeDetectionScheduler
Without Zone.js, Angular can only rely on its own APIs
These APIs include:
This triggers Angular change detection starting from v18
ChangeDetectionScheduler
New experimental Zoneless change detection provider
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
...
],
};
{
"name": "my-app",
"dependencies": {
"@angular/compiler": "^18.0.0",
"@angular/core": "^18.0.0",
...,
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}
}
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"polyfills": [
"zone.js"
],
},
...
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"polyfills": [
"zone.js"
"zone.js/testing"
],
},
...
Bye Bye Zone.js
App not working
TIME TO REFACTOR
Demo structure:
Grusp
🚀 DEMO 🚀
Steps to Get ready to go Zoneless:
Steps to Get ready to go Zoneless:
Steps to Get ready to go Zoneless:
Embrace the Signals APIs
@Component({ ... })
export class MyComponent {
myInputEl = viewChildren('inputEl'); // Signal<readonly ElementRef[]>
myContentEl = contentChild('contentEl'); // Signal<ElementRef | undefined>
myProp = input<string>(); // InputSignal<string, string>
myRequiredProp = input.required<string>(); // InputSignal<string, string>
myModel = model<string>(); // ModelSignal<string>
myRequiredModel = model.required<string>(); // ModelSignal<string>
}
OnPush
Skip checks unless
Performance
DevEx
Default
Checks all components binded values on every
event or model change
Bye bye Zone.js� �Easy to dev and highly optimized with Signals
Zoneless
DevEx
Performance
Davide Passafaro
davide-passafaro
DavidePassafaro
@DaveloperIT
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideExperimentalCheckNoChangesForDebug({
interval: 2000,
// useNgZoneOnStable: true,
exhaustive: true
}),
...
],
};
requestAnimationFrame
setTimeout
ChangeDetectionScheduler