Использование Angular Async Pipe для подписки на Observable

Я успешно подписываюсь на наблюдаемое в нескольких различных компонентах моего приложения Angular / Ionic. Однако, поскольку я делаю это вручную, это также означает, что мне нужно вручную отказаться от подписки на них, что я в настоящее время делаю в моем крючке жизненного цикла ngOnDestroy().

Реализация кода компонента, которая у меня работает, которая работает, выглядит так:

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

Код моего представления для компонента выглядит так:

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

А вызываемый здесь сервисный метод выглядит так:

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

То, что я хотел бы сделать, но мне трудно приступить к работе, - это использовать канал async для подписки в моем представлении HTML. Это было бы чище, потому что тогда мне не пришлось бы быть таким многословным, и мне не пришлось бы вручную подписываться и отказываться от подписки в компонентах, где я это использую. Насколько я понимаю, это также рекомендуемый способ обработки этих сценариев именно по этим причинам.

Как бы выглядел этот код? Я пробовал это, но это не сработало:

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

И в моем представлении HTML я сделал это:

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

Но, как я уже сказал, это не работает. Что мне здесь нужно изменить? Как я могу вызвать метод service getPlayerStats(), но обработать подписку в представлении компонентов с помощью async pipe?


person Rey    schedule 23.07.2020    source источник


Ответы (1)


Вы можете попробовать использовать подпись as директивы *ngIf. Он также позволяет использовать уведомление несколько раз с одним async. Предполагая, что наблюдаемый объект является HTTP-вызовом, помните, что каждый канал async будет запускать новый запрос, поскольку это холодный наблюдаемый объект.

Обычно к наблюдаемой переменной добавляется знак доллара. Итак, следуя ему, контроллер будет

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`
    }
  );
}

И шаблон, использующий эту переменную, будет

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

Само собой разумеется, но для того, чтобы это сработало, функцию getPlayerStatus() нужно вызвать хотя бы один раз для инициализации переменной this.user$.

person Michael D    schedule 23.07.2020
comment
В какой-то степени это сработало, но все же дало мне несколько неожиданных результатов. Пожалуйста, смотрите мое обновление выше. - person Rey; 24.07.2020
comment
@Rey: (user | async ) as user неверно. Интерполяция может ошибочно использовать наблюдаемое вместо уведомления. Либо используйте доллар, как показано в ответе, либо другие имена: (user | async ) as userDetails. - person Michael D; 24.07.2020
comment
Хорошо, понял. И на самом деле, из вашего обновленного ответа я понимаю, что мне не хватало pipe(). Это был ключевой недостающий элемент. Большое спасибо, @Michael D. - person Rey; 24.07.2020