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.
Top comments (2)
Being Doing Angular Development for quite a time but I think that using the RXJS for a Simple task is not a good idea. The RXJS is not built or should be used for small things and tasks.
The Area where React outsmarts the Angular is the Template-Based Rendering, as it takes very little time to reload the application, and Angular uses the Template base Rendering.
Thanks for the response! I don't disagree with you at all. I should have probably prefaced this in the post, but the example above was just for educational purposes.
It is perfectly capable of handling this and if you're using
OnPush
detection strategy I think you'll find RXJS manages this just fine.React is faster at template rendering because of the virtual DOM it is uses as opposed to Angular's use of the actual DOM, but React also only has one-way binding so I'm not entirely sure how that would compare in a situation like this because this post is referring to the two-way binding of the change detection system in Angular.