Reputation: 17762
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?
Upvotes: 53
Views: 16146
Reputation: 1692
Neither, you should compose your application as smart and presentation components.
Advantages:
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:
Upvotes: 47
Reputation: 3922
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.
Upvotes: 22
Reputation: 504
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/
Upvotes: 14