我如何在 RXJS 和 Angular 中订阅或合并新的 Observable

How can I subscribe or merge new Observable in RXJS and Angular

我有一项服务总是 returns Observable<T>,我无法更改此服务的代码。

假设我有一个按钮,每当单击该按钮时,我都会调用服务中的方法,它 returns 一个新的 Observable。如何将新数据更新为 UI?

Source code and playground on 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>

一种简单的方法是将 Observable 转换为 Promise 并使用 await:

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

如果 Observable 发出不止一次并且您想要所有值,请使用 .subscribe()。

我强烈建议使用反应式方法:

HTML

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

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

ts

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 时,您的 public class 属性 random$ 将从您的服务中存储 observable,然后,在你的模板 html 中,使用 async 管道,你订阅 random$,它将对每个 click 事件做出反应,这样,你可以保持你的 ts 文件更干净简单

现在,如果出于某种原因,您需要在 ts 文件中包含该随机数,您可以 pipe observable 并仍然保持这种被动方法:

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