当另一个可观察对象发生变化时,我如何 re-call 一个 Angular HttpClient 可观察对象?

How can I re-call an Angular HttpClient observable when another observable changes?

大局,我想要实现的是一组过滤器我的页面 header 控制我应用程序中多个分析页面的输入参数。我已将过滤器的功能封装到一个 Angular 服务中,该服务公开一个在过滤器发生更改时发出的可观察对象。

我想要的是在 HttpClient 请求中使用这些过滤器值来订阅过滤器更改的服务,并且 re-run 在过滤器更改时使用它们的 HttpClient 请求(例如,如果日期范围发生更改,任何我页面上由该日期范围驱动的元素会自动更新)。

我的应用程序中的典型数据服务如下所示。看起来我正在尝试做的事情应该足够简单,但我正在努力了解 RxJS 库,以便以我的目标方式组合可观察对象。

export class DashboardDataService {

  constructor(
    private readonly http: HttpClient,
    private readonly globalFiltersService: GlobalFiltersService
  ) { }

  public getDashboard(): Observable<DashboardDto> {

    const filtersSubscription = globalFiltersService.filters$.subscribe(...);

    const observable = this.http.get<DashboardDto>(`${environment.apiBase}network/dashboard`, {
      params: this.globalFiltersService.getHttpParams()
    });

    // TODO: when filtersSubscription receives new data, make observable re-run it's HTTP request and emit a new response

    return observable; // Make this observable emit new data 
  }

}

我正在使用 Angular 8 和 RxJS 6,因此最好采用最现代的方式。

更新:工作实施

export class GlobalFiltersService {

  private readonly _httpParams$: BehaviorSubject<{ [param: string]: string | string[]; }>;
  private start: Moment;
  private end: Moment;

  constructor() {
    this._httpParams$ = new BehaviorSubject(this.getHttpParams());
  }

  public setDateFilter(start: Moment, end: Moment) {
    this.start = start;
    this.end = end;
    this._httpParams$.next(this.getHttpParams());
  }

  public get httpParams$() {
    return this._httpParams$.asObservable();
  }

  public getHttpParams() {
    return {
      start: this.start.toISOString(),
      end: this.end.toISOString()
    };
  }

}

export class DashboardDataService {

  private _dashboard$: Observable<DashboardDto>;

  constructor(
    private readonly http: HttpClient,
    private readonly globalFiltersService: GlobalFiltersService
  ) { }

  public getDashboard(): Observable<DashboardDto> {
    if (!this._dashboard$) {
      // Update the dashboard observable whenever global filters are changed
      this._dashboard$ = this.globalFiltersService.httpParams$.pipe(
        distinctUntilChanged(isEqual), // Lodash deep comparison. Only replay when filters actually change.
        switchMap(params => this.http.get<DashboardDto>(`${environment.apiBase}network/dashboard`, { params })),
        shareReplay(1),
        take(1)
      );
    }
    return this._dashboard$;
  }

}

export class DashboardResolver implements Resolve<DashboardDto> {

  constructor(private readonly dashboardDataService: DashboardDataService, private readonly router: Router) {}

  public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<DashboardDto> {
    return this.dashboardDataService.getDashboard();
  }

}

尝试以下方法:

import {map, switchMap, shareReplay } from 'rxjs/operators';

export class FooComponent {
  readonly dashboard$: Observable<DashboardDto>;

  ctor(...){
    this.dashboard$ = this.globalFiltersService.filters$.pipe(
      // map filter event to the result of invoking `GlobalFiltersService#getParams`
      map(_ => this.globalFiltersService.getHttpParams()),
      // maps the params to a new "inner observable" and flatten the result.
      // `switchMap` will cancel the "inner observable" whenever a new event is
      // emitted by the "source observable"
      switchMap(params => this.http.get<DashboardDto>(`${environment.apiBase}network/dashboard`, { params })),
      // avoid retrigering the HTTP request whenever a new subscriber is registered 
      // by sharing the last value of this stream
      shareReplay(1)
    );
  }
}

好问题!我必须同步 URL 参数、表单和查询结果。它把我带到了状态管理的架构兔子洞。

TL;DR 当有许多元素依赖于最新数据时,您需要在该数据上具有高度可访问的状态。解决方案更多的是关于架构而不是使用哪种 RXJS 方法。

这是我构建的服务示例stackblitz.com/edit/state-with-simple-service

这是我的要求。 (直接适用于问题的)

  1. 共享所有选项的状态(从表单 components/URL 收到)
  2. 同步选项与 URL 参数
  3. 将所有表单与选项同步
  4. 查询结果
  5. 分享结果

要点如下:

export class SearchService {
    // 1. results object from the endpoint called with the current options
    private _currentResults = new BehaviorSubject<any[]>([]);

    // 2. current state of URL parameters and Form values
    private _currentOptions = new BehaviorSubject<Params>({});

    // 3. private options object to manipulate
    private _options: Params = {};

然后使用 getter 访问这些:

// Any component can subscribe to the current results from options query
public get results(): Observable<any[]> {
    return this._currentResults.asObservable();
}
// Any component can subscribe to the current options
public get options(): Observable<Params> {
    return this._currentOptions.asObservable();
}

随时用next()

更新私人主题
this._currentOptions.next(this._options);

现在无需引入像 redux 这样庞大的框架就可以进行状态管理了。