Easy way to understand change detection in angular with example
Angular’s change detection is a mechanism that allows the framework to automatically detect and update the view whenever there is a change in the application data. It’s a core part of Angular’s functionality that enables the framework to maintain a reactive and interactive user interface.
Angular’s change detection system operates by comparing the current and previous states of the application data. When there is a change in the data, the change detection mechanism triggers a re-evaluation of the view, updating it with the new data.
Angular offers two modes of change detection: default and OnPush. The default change detection mode checks for changes in all component properties and their nested properties. In contrast, the OnPush mode is an optimization technique that limits the change detection to the components with an @Input decorator, or that use immutable objects and observables.
The change detection process can be triggered in three ways:
- Default change detection: This is the default change detection strategy used by Angular. In this strategy, Angular checks for changes to the component’s properties and inputs on every change detection cycle. This can be resource-intensive and can slow down the application in large and complex applications. Here’s an example:
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<button (click)="increment()">Increment</button>
<p>{{ count }}</p>
`
})
export class CounterComponent {
count = 0;
increment() {
this.count++;
}
}
In the above code, the CounterComponent
has a count
property that is incremented every time the increment
method is called. Since the default change detection strategy is used, Angular will detect and update the view on every change detection cycle.
OnPush change detection: The OnPush change detection strategy is an optimization that can improve the performance of an application by reducing the number of change detection cycles. In this strategy, Angular only checks for changes to the component’s inputs and properties if the reference to the input or property has changed. Here’s an example:
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-person',
template: `
<p>{{ name }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonComponent {
@Input() name: string;
}
Manually triggering change detection: This can be achieved by calling the ChangeDetectorRef.detectChanges() method in the component code.
@Component({
selector: 'app-counter',
template: `
<button (click)="increment()">Increment</button>
<p>{{ count }}</p>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CounterComponent {
count = 0;
constructor(private ref: ChangeDetectorRef) {}
increment() {
setInterval(() => {
this.count++;
// require view to be updated
this.ref.markForCheck();
}, 1000);
}
}
In this above example, we are using the push strategy that means CheckOnce, rather than the default CheckAlways, then forces a second check after an interval
In Angular, we can manually trigger change detection using the detectChanges() method of the ChangeDetectorRef service. The ChangeDetectorRef service is available in every Angular component and provides several methods for manually triggering change detection:
- detectChanges(): This method triggers a change detection cycle for the component and its children.
- markForCheck(): This method marks the component and its children for change detection but doesn’t trigger a change detection cycle immediately. Angular will trigger change detection during the next change detection cycle.
- detach(): This method detaches the component from the change detection tree, preventing it from being checked for changes. This can be useful when we have a component that doesn’t need to be updated frequently.
- reattach(): This method reattaches the component to the change detection tree, allowing it to be checked for changes again.
Here’s an example of manually triggering change detection:
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-my-component',
template: `
<ul>
<li *ngFor="let item of items">{{ item }}</li>
</ul>
<button (click)="addItem()">Add Item</button>
`
})
export class MyComponent implements OnInit {
items: string[];
constructor(private dataService: DataService, private cdr: ChangeDetectorRef) {}
ngOnInit() {
this.dataService.getItems().subscribe((newItems: string[]) => {
this.items = newItems;
this.cdr.detectChanges(); // manually trigger change detection
});
}
addItem() {
this.dataService.addItem('New Item');
this.cdr.markForCheck(); // mark component for change detection
}
}
In the above code, the MyComponent retrieves items from a DataService asynchronously and displays them in a list. When the user clicks the “Add Item” button, we add a new item to the list and mark the component for change detection using the markForCheck() method.
In summary, we can manually trigger change detection in Angular using the detectChanges() method of the ChangeDetectorRef service. Additionally, the markForCheck(), detach(), and reattach() methods provide additional flexibility for controlling when change detection is triggered.
It’s worth noting that Angular’s change detection mechanism is highly performant, thanks to its ability to detect only the changes that occur in the application data. However, it’s important to be mindful of the number of components that are being checked for changes, as this can impact the performance of the application. In some cases, optimizing the change detection strategy can lead to significant performance gains in large applications.