The change detectors serve as a mechanism for monitoring both the past and present states of a component, along with its structural elements, enabling Angular to be notified of any alterations. When Angular receives notification from a change detector, it directs the associated component to refresh and synchronize the DOM accordingly.

Angular applications consist of a hierarchical structure of components. During runtime, Angular dynamically generates a distinct change detector class for each component within this tree, mirroring the hierarchical arrangement of components.

When a change detection event occurs, Angular traverses this tree of change detectors to identify any reported modifications. The process entails a sequential descent from the root change detector downward. This design ensures that the change detection cycle executes once for every identified alteration, maintaining predictability in updating the model. This predictability is rooted in the understanding that component data originates solely from its parent, reinforcing the clarity of the sequential approach.

Why Do We Need It?

If you’re just tinkering with a small project or showing off a simple demo, Angular’s automatic change detection should do the job fine. But if you’re tackling a big business or organization’s app, things might slow down. This happens because Angular keeps an eye on every little change in the display by default. One way to speed things up is by switching to manual change detection, where you decide what gets checked and when. However, it’s not always the smoothest solution.

What Is It?

A true “manual” change detection approach in Angular involves utilizing the OnPush change detection strategy along with methods such as detectChanges(), detach(), markForCheck(), and others within your components. This method grants you extensive control over the change detection process.

For example, If you’re receiving data from a websocket, using detectChanges() allows you to regulate how frequently you’re scanning for changes, thus optimizing performance. However, it’s worth noting that adopting this approach can introduce complexity, especially if your application doesn’t necessitate such fine-grained control.

The most effective and straightforward method to ensure optimal performance in your Angular application is to utilize a combination of the OnPush change detection strategy and immutable data. When OnPush is activated, Angular doesn’t scrutinize each template expression for changes. Instead, It only detects alterations to @Input values as a whole, particularly if the input is an object. Hence, employing immutable data is crucial to maintain this efficient approach.

How Does It Work?

With OnPush, Angular will only initiate checks “upon notification.” Here are the four scenarios that trigger this notification:

An @Input reference has changed
This implies that whenever bound data (such as an @Input) undergoes a change, Angular examines the object reference rather than individual properties. Hence, adopting immutable data is essential. With immutable data, each alteration results in creating a new copy with a distinct object reference.

The component or its children raise an event

Raise an event within the component
@Component({
  selector: 'my-component',
  template: `
    <button (click)="saveForm.emit(form.value)"
  `
})
export class MyComponent {
  @Output() saveForm = new EventEmitter<Item>();
}

 
A bound observable in the template changes

{{ myObservable$ | async }}

Manual
Calling detectChanges() within a component promptly triggers change detection for that component and its children. Conversely, markForCheck() temporarily includes the component and its children in Angular’s default change detection cycle for a single iteration.

Useful Methods

ChangeDetectorRef.detach()
Detaching a view from the change-detection tree means it won’t undergo checks until reattached. Pair with detectChanges() for localized change detection.
Detached views remain unchecked during change detection cycles until reattachment, even if flagged as dirty.

ChangeDetectorRef.detectChanges()
Performs change detection on this view and its child components. Pair with ChangeDetectorRef.detach() for localized change detection checks.

ChangeDetectorRef.markForCheck()
When a view employs the ChangeDetectionStrategy.OnPush (checkOnce) strategy, this method explicitly flags the view as changed, prompting it to undergo reevaluation.
Components typically become flagged as dirty (requiring re-rendering) when inputs change or events occur within the view. Use this method to ensure a component is rechecked even in the absence of these triggers.

ChangeDetectorRef.reattach()
Reattaches a previously detached view to the change detection tree. Views are initially attached to the tree by default.

Using Zone.js

In Angular, Zone.js plays a vital role in managing change detection by creating a dedicated zone for each component.

Whenever a component is instantiated, Angular establishes a new zone specifically for that component. This zone keeps track of any modifications occurring within the component and triggers the appropriate change detection processes when necessary.

Here’s a practical example of how Zone.js is integrated into an Angular application:

import { Component, NgZone } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    
    
{{ message }}
`, }) export class AppComponent { message = 'Hello'; constructor(private ngZone: NgZone) {} onClick() { this.ngZone.run(() => { this.message = 'World'; }); } }

In the example provided above, we’ve crafted a straightforward Angular component featuring a button and a message. Upon clicking the button, the message dynamically changes to “World”. We’ve harnessed the NgZone service to establish a new zone tailored to the component and execute the onClick function within that zone.

Angular furnishes the NgZone service, empowering developers to execute code within or outside the Angular zone. Below is a demonstration:

import { Component, NgZone } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
    
  `,
})
export class AppComponent {
  constructor(private zone: NgZone) {}
  handleClick() {
    this.zone.runOutsideAngular(() => {
      // Code to run outside of the Angular zone
    });
  }
}

In this example, we have an Angular component with a button. When the button is clicked, we use the NgZone service to run some code outside of the Angular zone. Inside the handleClick() method, we call the runOutsideAngular() method of the NgZone service, and pass in a function to be executed outside of the Angular zone.

Any code that we want to run outside of the Angular zone should be placed inside this function. This can be useful when we want to perform some long-running or resource-intensive operation, such as interacting with a third-party library, that we don’t want to trigger change detection in the component tree.

Similarly, we can also use the run() method of the NgZone service to run code inside the Angular zone. Here’s an example:

import { Component, NgZone } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `
    
  `,
})
export class AppComponent {
  constructor(private zone: NgZone) {}
  handleClick() {
    this.zone.run(() => {
      // Code to run inside the Angular zone
    });
  }
}

In this example, we use the run() method from the NgZone service to execute code within the Angular zone. Within the handleClick() method, we employ NgZone’s run() method and provide a function to be executed within Angular’s zone.

Any code intended to run within Angular’s zone should be encapsulated within this function. This practice proves valuable when we need to trigger change detection within the component tree or interact with Angular-specific features like @Input() and @Output().

By leveraging the NgZone service and its run() and runOutsideAngular() methods, we gain control over when and where change detection occurs in our Angular applications, thereby optimizing performance and responsiveness.

Using ApplicationRef.tick()

The Application Ref holds a reference to the root view and offers the ability to manually trigger change detection via the tick function.

Here is the Example:
component.ts

@Component ({
   selector: 'my-app',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.css'],
   changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
   counter = 1;

   constructor (private appRef: ApplicationRef) {
     setInterval (() => this.counter ++, 1000);
   }

   updateApplication() {
     this.appRef.tick();
   }
}

component.html

{{counter}}

In the given example, each time we click the “UPDATE APP” button, we invoke the tick() function, which triggers an update for every child component, beginning from the root of the application.

In our scenario, this action will solely update the counter variable.

Using AngularJS

AngularJS provides developers with a solution to this problem through its force change detection mechanism. By explicitly triggering the digest cycle, developers can prompt Angular to reevaluate expressions and update the view accordingly. This mechanism ensures that any changes made outside of Angular’s context are accounted for, maintaining the application’s integrity.

There are several ways to trigger change detection in AngularJS:

$apply()
The $apply() function is used to explicitly start the digest cycle. It takes a function as an argument, which Angular will execute and then trigger the digest cycle.
$scope.$apply(function() {
// Perform operations that modify the model
});

$digest()
Unlike $apply(), which triggers a digest cycle for the entire application, $digest() only evaluates the current scope and its children. This can be useful in scenarios where you want to limit the scope of change detection.
$scope.$digest();

$timeout()
AngularJS’s $timeout() service allows you to schedule a function to be executed in the next digest cycle. This can be beneficial when you need to defer operations that modify the model.
$timeout(function() {
// Perform operations that modify the model
});

Support On Demand!

Angular