Rey
Rey

Reputation: 1433

Using Angular Async Pipe to Subscribe to Observable

I am successfully subscribing to an observable in a couple of different components in my Angular/Ionic app. However, because I am doing so manually, that also means I need to manually unsubscribe to them, which I currently do in my ngOnDestroy() life cycle hook.

The component code implementation I have, that IS working, looks like this:

  getPlayerStats() {
    this.playerSubscription = this.mainService.getPlayerStats(this.username).subscribe(
      user => {
        this.user = user;
      },
      error => this.errorMsg = error
    );
  }

My view code for the component looks like this:

<ion-card *ngIf="user">
  ... display user info
</ion-card>

And the service method that is called here looks like this:

  getPlayerStats(username: string) {
    const req = `users/stats/${username}`;
    return this.apiService.sendRequest(req);
  }

What I'd like to do, but am having difficulty getting to work, is using the async pipe do the subscribing in my HTML view. That'd be cleaner because then I wouldn't have to be so verbose and I wouldn't have to manually subscribe and unsubscribe in the components where I use this. My understanding is that this is also the recommended way of handling these scenarios, for precisely these reasons.

What would that code look like? I tried this but it did not work:

getPlayerStatus() {
  this.user = this.mainService.getPlayerStats(this.username);
}

And in my HTML view I did this:

<ion-card *ngIf="user | async">
  ... display user info
</ion-card>

But as I say, this isn't working. What do I need to change here? How can I call the service getPlayerStats() method, but handle the subscribing in the component view with the async pipe?

Upvotes: 1

Views: 2117

Answers (1)

Barremian
Barremian

Reputation: 31125

You could try to use the *ngIf directive's as signature. It also allows you to use the notification multiple times with a single async. Assuming that the observable is an HTTP call, remember that each async pipe would trigger a new request since it's a cold observable.

Usual convention is to suffix an observable variable with a dollar sign. So following it, the controller would be

user$: any;

getPlayerStatus() {
  this.user$ = this.mainService.getPlayerStats(this.username).pipe(
    catchError(error => {       // <-- HTTP error handling 
      this.errorMsg = error;
      return of(error);         // <-- return an observable from `catchError`
    }
  );
}

And the template using this variable would be

<ng-container *ngIf="(user$ | async) as user">
  <ion-card>
    ... display user info
    {{ user | json }}
    {{ user.name }}
    ...
  </ion-card>
</ng-container>

Also it goes without saying, but for this to work, the getPlayerStatus() function should be called at least once to initialize the this.user$ variable.

Upvotes: 4

Related Questions