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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Picci
  • 16,775
  • 13
  • 70
  • 113
  • 2
    You might want to consider using the async pipe in containers on inputs for 'dumb' components. See components and containers in the [`example-app`](https://github.com/ngrx/example-app/tree/master/src/app) and also [*Presentational and Container Components*](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0). – cartant Jan 17 '17 at 22:30

3 Answers3

46

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:

AmbrosiaDevelopments
  • 2,576
  • 21
  • 28
Victor Godoy
  • 1,642
  • 15
  • 18
  • 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 advance – Picci Jan 18 '17 at 07:45
  • Exactly the same information I wanted to ask for. Thanks a bunch! – Pramodh Valavala Jun 30 '17 at 04:45
  • good answer but how it would be useful when using child components with router? – Mohammad Rafigh Sep 05 '17 at 19:32
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.

marxin
  • 3,692
  • 3
  • 31
  • 44
  • 1
    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. :) – JoshuaTree Feb 28 '18 at 15:12
  • how do I access `per` in the controller? I guess now way? – Toolkit Aug 18 '18 at 08:26
13

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/

John Hamm
  • 474
  • 4
  • 11