Angular's Change Detection
Tags: angular, javascript
Published: 2020-04-15 12:43:37

During the Angular lifecycle, all components are susceptible to Angular's incredible change detection system. Any time something changes in your app, Angular makes sure to run through it's change detection (which is extremely performant all things considered) but this process can become a bottleneck in complex web applications. Fear not! There is a way to circumvent the normal change detection where Angular will review every single element in it's reaches, potentially bogging down your web app. 

In your component decorator: 

```
@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponentComponent {
```

By updating your change detection strategy, you've effectively told Angular that you don't want external changes to queue your component up for Angular to take a look at it. 

For example, let's assume you were making the famous todo list app. 

```
@Component({
  selector: 'app-todo-list',
  templateUrl: './todo-list.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoListComponent {

  todos = ['Read Drew's post.', 'Review the post'];

  addTodo(todo) {
    this.todos.push(todo);
  }

}

/** and this is your template (todo-list.component.html) **/

<input #newTodo type="text" placeholder="Enter a todo">
<button type="button" (click)="addTodo(newTodo.value)">Add Todo</button>

<ul>
  <li *ngFor="let todo of todos">{{ todo }}</li>
</ul>
```

With the default change detection (ChangeDetectionStrategy.Default), you would expect adding a todo to update the list. 

However, what happens above in our scenario where we've used `OnPush` for our change detection strategy? You might be surprised to learn that it doesn't update the list in our DOM, although we could `console.log(todos)` and see that it definitely added the our new todo to that list. 

The fix? Rxjs. We need to show Angular a new reference to a new array in that list on the DOM for it to change the view. Like this: 

```
@Component({
  selector: 'app-todo-list',
  templateUrl: './todo-list.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoListComponent {

  private todos = ['Read Drew's post.', 'Review the post'];
  todos$ = new BehaviorSubject<string[]>(todos);

  addTodo(todo) {
    this.todos.push(todo);
    this.todos$.next(this.todos);
  }

}

/** and this is your template (todo-list.component.html) **/

<input #newTodo type="text" placeholder="Enter a todo">
<button type="button" (click)="addTodo(newTodo.value)">Add Todo</button>

<ul>
  <ng-container *ngFor="let todo of todos$|async">
    <li>{{ todo }}</li>
  </ng-container>
</ul>
```

What happens if you just can't get something to update still? Angular enables us the ability to tap into the change detection system and to tell Angular explicitly when to update: 

```
@Component({
  selector: 'app-todo-list',
  templateUrl: './todo-list.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoListComponent {

  private todos = ['Read Drew's post.', 'Review the post'];

  constructor(private cd: ChangeDetectorRef) {}

  addTodo(todo) {
    this.todos.push(todo);
    this.cd.detechChanges();
  }

}
```

This would do essentially the same thing as our first example. Although it is still forcing Angular to run change detection which is just doing the same thing we were trying to avoid by using the `OnPush` change detection strategy in the first place. So, only use it when absolutely necessary.