当另一个可观察的变化时,如何调用可观察的Angular HttpClient?

时间:2019-06-12 14:58:58

标签: angular typescript rxjs angular-httpclient

大图,我要实现的目标是对我的页面标题进行过滤,以控制应用程序中多个分析页面的输入参数。我将过滤器的功能封装到Angular服务中,该服务提供了一个可观察到的东西,当对过滤器进行更改时,该可观察到的东西会发出。我想要的服务是使用HttpClient请求中的那些过滤器值来订阅过滤器中的更改并在过滤器发生更改时重新运行其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();
  }

}

2 个答案:

答案 0 :(得分:0)

尝试以下操作:

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

答案 1 :(得分:0)

好问题!我必须同步URL参数,表单和查询结果。这使我陷入了体系结构困境,一直到状态管理。

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

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

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

  1. 共享所有选项的状态(从表单组件/ 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 = {};

然后使用吸气剂访问它们:

// 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这样的大型框架就可以进行状态管理。