1

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?

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

3 Comments

This worked to some degree, but still gave me some unexpected results. Please see my update above.
@Rey: (user | async ) as user isn't correct. The interpolation might mistakenly use the observable instead of the notification. Either use the dollar as shown in the answer or use different names: (user | async ) as userDetails.
Okay, understood. And actually, from your updated answer, I realize what I was missing was the pipe(). That was the key missing piece. Many thanks, @Michael D.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.