Как я могу подписаться или объединить новые Observable в RXJS и Angular

У меня есть сервис, который всегда возвращает Observable<T>, и я не могу изменить код этого сервиса.

Предположим, у меня есть кнопка, и всякий раз, когда она нажимается, я вызываю метод в службе, и он возвращает новый Observable. Как я могу обновить новые данные в пользовательском интерфейсе?

Исходный код и игровая площадка на StackBlitz

app.component.ts

import { Component, Injectable, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';

@Injectable()
export class Service {
  // Cannot change the code in this class
  public getRandom(): Observable<number> {
    return of(Math.random());
  }
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  constructor(private service: Service) {}

  public random: number = 0;
  public random$: Observable<number> = new Observable<number>();

  ngOnInit(): void {
    this.random = 0;
  }

  buttonClick(): void {
    // how can I update the random with this.service.getRandom()?
    console.log('button clicked')
  }
}

app.component.html

<h1>{{random}}</h1>

<button (click)="buttonClick()">Get new Random number</button>
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Обработка ошибок при выполнении HTTP-запросов в JavaScript.
Обработка ошибок при выполнении HTTP-запросов в JavaScript.
Каждый проект должен выполнять HTTP-запросы, и, конечно, некоторые из этих запросов могут содержать ошибки. Нам нужно знать, как обрабатывать эти...
Включение UTF-8 в jsPDF с помощью Angular
Включение UTF-8 в jsPDF с помощью Angular
Привет, разработчики, я предполагаю, что вы уже знаете, как экспортировать pdf через jsPDF. Если ответ отрицательный, то вы можете ознакомиться с моей...
1
0
43
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Один простой способ — преобразовать Observable в Promise и использовать await:

async buttonClick(): Promise<void> {
    const value = await firstValueFrom(random$);
    console.log('button clicked ' + value);
}

Если Observable испускает более одного раза и вам нужны все значения, используйте .subscribe().

Почему вы рекомендуете преобразовать observable в promise? Вы не должны использовать promises в проекте Angular...

Andres2142 09.04.2022 19:17

Потому что концептуально они Полегче для понимания (у них ровно одно значение) и в 99% случаев достаточный (поскольку HTTP-запросы дают ровно одно значение). Вдобавок отписываться не нужно.

DasKrümelmonster 09.04.2022 19:33

Когда дело доходит до обработки запросов http, вы ни от чего не отписываетесь, потому что Angular делает это за вас, почему? из-за HttpClientModule, теперь, если вы создадите свой особенный observable, например, subject, там можно будет отписаться. Проверьте ответ, который я дал на этот вопрос, я не отписываюсь, в этом нет необходимости, и до сих пор использую observables

Andres2142 09.04.2022 19:57

Я согласился, что await, async и promise легче понять. Однако проект, над которым я работаю, использует rxjs и Angular. Для последовательности, я должен следовать.

Tuan Tran 09.04.2022 20:55

"Я не отписываюсь, в этом нет необходимости" Если это работает для вас, хорошо. В нашем проекте у нас было много проблем с многократными запросами (например, третье посещение определенного компонента вызывало один и тот же внутренний запрос 3 раза) или накоплением подписок до тех пор, пока пользовательский ноутбук не стал OOM. В конце концов, вы не видите, является ли Observable автоматически отписавшимся или нет. А в обзоре кода это, вероятно, еще труднее заметить. С тех пор мы используем шаблон «ngondestroy->unsubscribe» для каждой подписки. Утечки прекратились.

DasKrümelmonster 09.04.2022 21:23
Ответ принят как подходящий

Я настоятельно рекомендую использовать реактивный подход:

HTML

<ng-container *ngIf="(random$ | async) as theRandomNumber" >
  <h1>{{ theRandomNumber }}</h1>
</ng-container>

<button (click)="buttonClick()">Get new Random number</button>

тс

export class AppComponent implements OnInit {
  random$: Observable<number>!; // no need to initialize it

  constructor(private service: Service) {}
  
  ngOnInit(): void {}

  buttonClick(): void {
     this.random$ = this.service.getRandom();
  }
}

Когда вы инициируете событие click, ваше свойство публичного класса random$ будет хранить observable из вашего сервиса, затем в вашем HTML-шаблоне, используя канал async, вы подписываетесь на random$, и оно будет реагировать на каждое событие click, при этом вы Держите ваш ts-файл чище и проще

Теперь, если по какой-то причине вам нужно иметь это случайное число в вашем файле ts, вы можете pipeobservable и при этом сохранить этот реактивный подход:

import { tap } from 'rxjs';

export class AppComponent implements OnInit {
  random$: Observable<number>!; // no need to initialize it
  private random!: number;

  constructor(private service: Service) {}

  ngOnInit(): void {}

  buttonClick(): void {
     this.random$ = this.service.getRandom()
     .pipe(tap((theNumber) => this.random = theNumber));
  }
}

Мне нужно инициализировать random$ иначе angular не скомпилируется

Tuan Tran 09.04.2022 20:53

Другие вопросы по теме