NgrxStore and Angular - Use the async pipe massively or subscribe just once in the constructor

AngularNgrx

Angular Problem Overview


I am starting to look at ngrx Store and I see the convenience to use the Angular async pipe. At the same time I am not sure whether using the Angular async pipe massively is a good choice.

I make a simple example. Let's assume that in the same template I need to show different attributes of an object (e.g. a Person) which is retrieved from the Store.

A piece of template code could be

<div>{{(person$ | async).name}}</div>
<div>{{(person$ | async).address}}</div>
<div>{{(person$ | async).age}}</div>

while the component class constructor would have

export class MyComponent {
  person$: Observable<Person>;

  constructor(
    private store: Store<ApplicationState>
  ) {
      this.person$ = this.store.select(stateToCurrentPersonSelector);
  }
.....
.....
}

As far as I understand this code implies 3 subscriptions (made in the template via the async pipe) to the same Observable (person$).

An alternative would be to define 1 property (person) in MyComponent and to have only 1 subscription (in the constructor) that fills the property, such as

export class MyComponent {
  person: Person;

  constructor(
    private store: Store<ApplicationState>
  ) {
      this.store.select(stateToCurrentPersonSelector)
                .subscribe(person => this.person = person);
  }
.....
.....
}

while the template uses standard property binding (i.e. without the async pipe), such as

<div>{{person.name}}</div>
<div>{{person.address}}</div>
<div>{{person.age}}</div>

Now the question

Is there any difference in terms of performance between the 2 approaches? Is the massive use of async pipe (i.e. a massive use of subscriptions) going to affect the efficiency of the code?

Angular Solutions


Solution 1 - Angular

Neither, you should compose your application as smart and presentation components.

Advantages:

  • All buissness logic on the smart controller.
  • Just one subscribe
  • Reusability
  • The presentation controller has only one responsibility, only to present data and does not know from where the data come from. (loosely coupled)

Answering the last question:

The massive use of async pipe will affect the efficiency, because it will subscribe to every async pipe. You can notice this more if you are calling a http service, because it will call the http request for each async pipe.

Smart Component

@Component({
  selector: 'app-my',
  template: `
      <app-person [person]="person$ | async"></app-person>
`,
  styleUrls: ['./my.component.css']
})
export class MyComponent implements OnInit {

    person$: Observable<Person>;

    constructor(private store: Store<ApplicationState>) {}

    ngOnInit() {
        this.person$ = this.store.select(stateToCurrentPersonSelector);
    }

}

Presentation Component

@Component({
  selector: 'app-person',
  template: `
    <div>{{person.name}}</div>
    <div>{{person.address}}</div>
    <div>{{person.age}}</div>
`,
  styleUrls: ['./my.component.css']
})
export class PersonComponent implements OnInit {

    @Input() person: Person;

    constructor() {}

    ngOnInit() {
    }

}

For more info check:

Solution 2 - Angular

Another possibility is to use construction like this:

<div *ngIf="person$ | async as per">
    <div>{{ per.name }}</div>
    <div>{{ per.address }}</div>
    <div>{{ per.age }}</div>
<div>

Although for reusable bits of code its possibily better to use presentation component method.

Please note this works in angular 5, not sure about other versions.

Solution 3 - Angular

You can add ".share()" to the end of any observable declarations. All of your async pipes on an observable will then share the same subscription:

this.name$ = Observable.create(observer => {
  console.log("Subscriber!", observer);
  return observer.next("john")
}).delay(2000).share();

this.httpget$ = http.get("https://api.github.com/").share();

Plunkr demonstrating: https://embed.plnkr.co/HNuq1jUh3vyfR2IuIe4X/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPicciView Question on Stackoverflow
Solution 1 - AngularVictor GodoyView Answer on Stackoverflow
Solution 2 - AngularmarxinView Answer on Stackoverflow
Solution 3 - AngularJohn HammView Answer on Stackoverflow