Reputation: 100010
Say I have this in my HTML:
<div>
<mat-label>Matched events count: {{(getMatchedEventsCount() | async)?}}</mat-label>
</div>
<div>
<mat-label>Total events count: {{(getTotalEventsCount() | async)?}}</mat-label>
</div>
my question is, what do I return (as far as an Observable) from these helper methods?
getMatchedEventsCount(){
return Observable.of(3);
}
getTotalEventsCount(){
return Observable.of(5);
}
but my question is - how can we do something actually asynchronous?
At the moment, I am getting this HTML parse error though:
Uncaught Error: Template parse errors: Parser Error: Unexpected end of expression: Matched events count: {{(getMatchedEventsCount() | async)?}} at the end of the expression [Matched events count: {{(getMatchedEventsCount() | async)?}}] in ng:///AppModule/EventsListComponent.html@40:21 (" [ERROR ->]Matched events count: {{(getMatchedEventsCount() | async)?}} "): ng:///AppModule/EventsListComponent.html@40:21 Parser Error: Conditional expression (getMatchedEventsCount() | async)? requires all 3 expressions at the end of the expression [Matched events count: {{(getMatchedEventsCount() | async)?}}] in ng:///AppModule/EventsListComponent.html@40:21 (" [ERROR ->]Matched events count: {{(getMatchedEventsCount() | async)?}} "): ng:///AppModule/EventsListComponent.html@40:21
Upvotes: 1
Views: 592
Reputation: 8306
I notice that you have already troubleshooted the ?
in the comment thread. The reason the ?
(called the "save navigation operator") does not work there is that it is guarding against null
and undefined
values in property paths, meaning you need to attempt to access a property after using the ?
. Right now you are trying to use it retroactively to see if an object is null
or undefined
, but it can only look forward into the object, not backwards, and it needs a property to look for.
You are correct that you should return an Observable
from the methods and give that to the async
pipe. Here is some documentation on the async
pipe just to be thorough: https://angular.io/api/common/AsyncPipe.
As to your question in the comment thread about how to use a Subscription
to load the data instead of an Observable
...
You can do that using the subscribe
method and assigning the data to a property on your component, like so:
matchedEventsSub: Subscription;
matchedEventsCount: number;
getMatchedEventsCount() {
this.matchedEventsSub = Observable.of(3).subscribe(value => {
this.matchedEventsCount = value;
});
}
Note that subscribe
ing to an Observable
returns a Subscription
. Then you must remember to unsubscribe
from that subscription in your OnDestroy
lifecycle hook to prevent memory leaks:
ngOnDestroy() {
if (this.matchedEventsSub) { this.matchedEventsSub.unsubscribe(); }
}
As you can imagine, this becomes cumbersome when you have 2, 3, 10 subscriptions in one component. That's why the Angular team created the async
pipe.
Finally,
how can we do something actually asynchronous?
It's actually quite simple. Let's say you have an EventsService
that you inject into your component:
constructor(private eventsService: EventService) {}
That service may encapsulate an Http
request or something -- Angular's HttpClient
module uses Observable
s to represent asynchronous data. You could use your EventsService
to get your asynchronous stream of events like this:
matchedEventsCount$: Observable<number>;
getMatchedEventsCount(): Observable<number> {
const allEvents$ = this.eventsService.getEvents();
return allEvents$.map(events => events.length);
}
Call the method in your OnInit
lifecycle hook to populate your data:
ngOnInit() {
this.getMatchedEventsCount();
}
And then display it in your template:
<h1>Events: {{ matchedEventsCount$ | async }}</h1>
Upvotes: 1