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

3

53

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?

Religieux answered 17/1, 2017 at 21:56 Comment(1)
You might want to consider using the async pipe in containers on inputs for 'dumb' components. See components and containers in the example-app and also Presentational and Container Components.Leathers
E
47

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:

Eyewash answered 17/1, 2017 at 23:26 Comment(3)
I appreciate the value of splitting smart and presentation components. It makes a lot of sense for all the reasons you have mentioned. If we just look at the 'code efficiency' side of the story, am I right in assuming that using one 'async pipe' and many 'property bindings' is more efficient than using 'many async pipes'? Thx in advanceReligieux
Exactly the same information I wanted to ask for. Thanks a bunch!Sanderson
good answer but how it would be useful when using child components with router?Stinko
C
22

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.

Chichi answered 12/2, 2018 at 10:27 Comment(2)
I like this because it fuses the smart and presentational components into a single block of code. I don't like this for the exact same reason. :)Schmaltz
how do I access per in the controller? I guess now way?Paroxysm
V
14

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/

Valero answered 23/1, 2017 at 20:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.