AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

Most of the popular Angular state management libraries like NgRx expose application state in a form of a stream of state objects. This is usually implemented with the help of RxJS Observables.

The state updates get pushed to the components which react by re-rendering their templates to display the most recent version of the application state.

There are multiple ways in which it is possible to consume this observable stream of state updates by the components, the two most popular being:

Using the subscribe() method and storing the state on the component instance, todos$.subscribe(todos => this.todos = todos) ... The | async pipe unwraps the state object directly in the component’s template, <li *ngFor=”let todo of todos$ | async”></li> ...

I have been thinking about this question and related trade-offs for quite some time now. I was mostly in favor of using subscribe() but couldn’t really point out exact reasons why.

This led to a need to come up with an overview of the situation while considering all the pros and cons to create a final guideline to be able to make objective decision in every case!

Before we dig deeper, I would like to thank simply10w and Tim Deschryver who provided lots of great input and feedback (PR) while working on Angular NgRx Material Starter project which implements ideas discussed in this article…

Topics

Please notice following things which make a big impact on the final answer to this question…

the way | async pipe works for the collection vs for singular objects

pipe works for the collection vs for singular objects possibility of using new-ish *ngIf “as” syntax (from Angular 4 and above)

“as” syntax (from Angular 4 and above) location of state handling logic (component’s class vs template)

OK, let’s do this!

Case 1: Use subscribe() in the ngOnInit method

Simple example of consuming observable stream of todos by unwrapping it in the component ngOnInit method and using unwrapped property in the template

👍 Advantages of using subscribe()

Unwrapped property can be used in multiple places in the template “as it is” without the need to rely on various workarounds as will be shown in the case 2 examples Unwrapped property is available everywhere in the component. This means it can be used directly in the component’s method without the need to pass it from the template. That way, all state can be kept in the component.

The only responsibility of the template is to trigger this method (eg using (click)=”toggleAllTodosToDone()”)

👎 Disadvantages of using subscribe()

Using of the subscribe() introduces complementary need to unsubscribe at the end of the component life-cycle to avoid memory leaks. Developers usually have to unsubscribe manually. The most RxJS (declarative) way to do this is to employ takeUntil(unsubscribe$) operator as shown in the example above. This solution is verbose and error prone because it is very easy to forget implementing ngOnDestroy which will not lead to any errors just a silent memory leak… Subscribing to the observable manually in the ngOnInit() doesn’t work with OnPush change detection strategy out of the box. We could make it work by using this.cd.markForCheck() inside of our subscribe handler but this is a very easy to forget, error prone solution.