Angular
  • Angular learning
  • Angular
    • Change Detection
      • Angular Change Detection Strategies
      • Understanding Change Detection Strategy in Angular
    • Angular Components Overview
      • Lifecycle hooks
      • View encapsulation
    • Text interpolation
    • Pipes
    • ARIA
    • Event binding
    • Directives
    • Dependency injection in Angular
    • Difference between Template-Driven and Reactive Forms
    • Guards
    • Resolvers
      • Resolver example
  • Memory management in Angular applications
  • Renderer2
  • Angular test
    • Testing
      • The different types of tests
      • Some testing best practices
      • Angular Service Testing in Depth
        • About Jasmine
        • Jasmine - Test suites
        • Implementation of First Jasmine Specfication
        • spyOn() & jasmine.createSpyObj()
        • beforeEach()
        • Testing services
        • Disabled and Focused Tests
        • flush
        • HttpTestingController
        • Sample code
      • Angular Component Testing in Depth
        • Intro to Angular Component testing
        • DOM interaction
        • Trigger Change Detection
        • Test Card List Test Suite Conclusion
        • Window.requestAnimationFrame()
        • Asynchronous Work (Jasmine)
        • Cooperative asynchronous JavaScript: Timeouts and intervals
        • FakeAsync - Asynchronous Work (Jasmine) part 2
        • tick()
        • Sample codes
      • Testing Promised-based code-intro Microtasks
        • Microtasks
        • Micro-tasks within an event loop (Summary)
        • Macro-tasks within an event loop (Summary)
        • Test promised Microtasks (code)
      • Using fakeAsync to test Async Observables
      • Cypress.io
        • Create our first e2e test
      • Angular CLI code coverage and deployment in prod mode.
      • Travis CI
  • Angular best practices
    • Angular best practices
      • Security
      • Accessibility in Angular
      • Keeping your Angular projects up-to-date
    • Bootstrapping an Angular Application
      • Understanding the File Structure
      • Bootstrapping Providers
    • Components in Angular
      • Creating Components
      • Application Structure with Components
        • Accessing Child Components from Template
        • Using Two-Way Data Binding
        • Responding to Component Events
        • Passing Data into a Component
      • Projection
      • Structuring Applications with Components
      • Using Other Components
  • Reactive extensions
    • RxJS
      • RxJS Operators
      • of
      • Observable
      • async pipe (Angular)
      • Interval
      • fromEvent
      • Pipe
      • Map
      • Tap
      • ShareReplay
      • Concat
      • ConcatMap
      • Merge
      • MergeMap
      • ExhaustMap
      • fromEvent
      • DebounceTime
        • Type Ahead
      • Distinct Until Changed
      • SwitchMap
      • CatchError
      • Finalize
      • RetryWhen
      • DelayWhen
      • ForkJoin
      • First
      • Interview Questions
      • Zip
  • NgRx
    • What's NgRx
      • Actions
      • Reducers
      • Selectors
      • 🙅‍♂️Authentication guard with NgRX
      • @ngrx/effects
        • Side-Effect refresh survivor
  • Interview Q&A
    • Angular Unit Testing Interview Questions
    • Angular Questions And Answers
  • Angular Advanced
    • Setting up our environment
      • Understanding Accessors (TS)
      • The host & ::ng-deep Pseudo Selector
Powered by GitBook
On this page
  • Simple Example
  • ChangeDetectorRef

Was this helpful?

  1. Angular
  2. Change Detection

Understanding Change Detection Strategy in Angular

Angular performs change detection on all components (from top to bottom) every time something changes in your app from something like a user event or data received from a network request. Change detection is very performant, but as an app gets more complex and the amount of components grows, change detection will have to perform more and more work. There’s a way to circumvent that however and set the change detection strategy to OnPush on specific components. Doing this will instruct Angular to run change detection on these components and their sub-tree only when new references are passed to them versus when data is simply mutated.

Simple Example

It’s probably easier to explain change detection with a clear example, so let’s start with a component that looks like this:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  foods = ['Bacon', 'Lettuce', 'Tomatoes'];

And the template looks like this:

<!--app.component.html-->
<input #newFood type="text" placeholder="Enter a new food">
<button (click)="addFood(newFood.value)">Add food</button>

<app-child [data]="foods"></app-child>

Our here’s our child component and template:

// child.component.ts
import { Component, Input } from '@angular/core';
<!--child.component.html-->
<ul>
  <li *ngFor="let item of data">{{ item }}</li>
</ul>
// child.component.ts
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';

With this, things don’t seem to work anymore. The new data still gets pushed into our foods array in the parent component, but Angular does not see a new reference for the data input and therefore doesn’t run change detection on the component.

To make it work again, the trick is to pass a completely new reference to our data input. This can be done with something like this instead of Array.push in our parent component’s addFood method:

addFood(food) {
  this.foods = [...this.foods, food];
}

With this variation, we are not mutating the foods array anymore, but returning a completely new one. Et voilà, things are working again in our child component! Angular detected a new reference to data, so it ran its change detection on the child component.

ChangeDetectorRef

When using a change detection strategy of OnPush, other than making sure to pass new references every time something should change, we can also make use of the ChangeDetectorRef for complete control.

ChangeDetectorRef.detectChanges()

We could for example keep mutating our data, and then have a button in our child component with a refresh button like this:

import { Component,
         Input,
         ChangeDetectionStrategy,
         ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
  @Input() data: string[];

  constructor(private cd: ChangeDetectorRef) {}

  refresh() {
    this.cd.detectChanges();
  }
}

And now when we click the refresh button, Angular runs change detection on the component.

ChangeDetectorRef.markForCheck()

Let’s say your data input is actually an observable. Let’s demonstrate with example using a RxJS Behavior Subject

// app.component.ts
import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

@Component({ ... })
export class AppComponent {
  foods = new BehaviorSubject(['Bacon', 'Letuce', 'Tomatoes']);

And we subscribe to it in the OnInit hook in our child component. We’ll add our food items to a foods array here:

// child.component.ts
import { Component,
         Input,
         ChangeDetectionStrategy,
         ChangeDetectorRef,
         OnInit } from '@angular/core';

import { Observable } from 'rxjs/Observable';
@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
  @Input() data: Observable<any>;
  foods: string[] = [];
  constructor(private cd: ChangeDetectorRef) {}

This would normally work right out of the box the same as our initial example, but here new data mutates our data observable, so Angular doesn’t run change detection. The solution is to call ChangeDetectorRef’s markForCheck when we subscribe to our observable:

ngOnInit() {
  this.data.subscribe(food => {
    this.foods = [...this.foods, ...food];
    this.cd.markForCheck();
  });
}

markForCheck instructs Angular that this particular input should trigger change detection when mutated.

ChangeDetectorRef.detach() and ChangeDetectorRef.reattach()

Yet another powerful thing you can do with ChangeDectorRef is to completely detach and reattach change detection manually with the detach and reattach methods.

PreviousAngular Change Detection StrategiesNextAngular Components Overview

Last updated 4 years ago

Was this helpful?

Everything works as expected and new food items get added to the list, thanks to in the child component that receives its data from the parent. Now let’s set the change detection strategy in the child component to OnPush:

This one area where using something like for state management can really become powerful, because most components can adopt an OnPush strategy, and ngrx will dispatch new references when data changes.

🚀 And there you have it! An easy way to tune your app’s performance. If you want to dig deeper into your understanding of change detection, I recommend this by Pascal Precht of thoughtram.

our input
ngrx/store
very informative post