Angular 当另一个可观察对象发生变化时,我如何重新调用一个角度可观察的HttpClient?

Angular 当另一个可观察对象发生变化时,我如何重新调用一个角度可观察的HttpClient?,angular,typescript,rxjs,angular-httpclient,Angular,Typescript,Rxjs,Angular Httpclient,总而言之,我试图实现的是一套过滤我的页面标题,它控制我应用程序中几个分析页面的输入参数。我已经将过滤器的功能封装到一个角度服务中,该服务公开了当过滤器发生变化时发出的可观测信息 我想要的是那些在HttpClient请求中使用这些筛选器值的服务订阅筛选器中的更改,并在筛选器更改时重新运行它们的HttpClient请求(例如,如果某个日期范围更改,我的页面上由该日期范围驱动的任何元素都会自动更新) 我的应用程序中的典型数据服务如下所示。看起来我试图做的事情应该足够简单,但我正努力让我的头脑充分了解R

总而言之,我试图实现的是一套过滤我的页面标题,它控制我应用程序中几个分析页面的输入参数。我已经将过滤器的功能封装到一个角度服务中,该服务公开了当过滤器发生变化时发出的可观测信息

我想要的是那些在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 
  }

}
导出类仪表板数据服务{
建造师(
专用只读http:HttpClient,
专用只读globalFiltersService:globalFiltersService
) { }
public getDashboard():可观察{
const filtersSubscription=globalFiltersService.filters$.subscription(…);
const observable=this.http.get(`${environment.apiBase}网络/dashboard`{
params:this.globalFiltersService.getHttpParams()
});
//TODO:当filtersSubscription接收到新数据时,使observable重新运行其HTTP请求并发出新响应
return observable;//使此observable发出新数据
}
}
我使用的是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();
  }

}
导出类GlobalFiltersService{
私有只读_httpParams$:行为主体;
私人开始:时刻;
私人目的:时刻;
构造函数(){
this.httpParams$=新行为子对象(this.getHttpParams());
}
公共setDateFilter(开始:时刻,结束:时刻){
this.start=start;
this.end=end;
this.u httpParams$.next(this.getHttpParams());
}
公共获取httpParams$(){
返回此项。_httpParams$.asObservable();
}
公共getHttpParams(){
返回{
start:this.start.toISOString(),
end:this.end.toISOString()
};
}
}
导出类DashboardDataService{
私有_dashboard$:可观察;
建造师(
专用只读http:HttpClient,
专用只读globalFiltersService:globalFiltersService
) { }
public getDashboard():可观察{
如果(!this.\u dashboard$){
//每当更改全局筛选器时,更新仪表板可观察项
this.\u dashboard$=this.globalFiltersService.httpParams$.pipe(
distinctUntilChanged(isEqual),//进行深度比较。仅在筛选器实际更改时重播。
switchMap(params=>this.http.get(`${environment.apiBase}network/dashboard`,{params})),
共享重播(1),
采取(1)
);
}
退回这个。_dashboard$;
}
}
导出类DashboardResolver实现解析{
构造函数(专用只读仪表板数据服务:仪表板数据服务,专用只读路由器:路由器){}
公共解析(路由:ActivatedRouteSnapshot,状态:RouterStateSnashot):可观察{
返回此.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)
    );
  }
}
从'rxjs/operators'导入{map,switchMap,shareReplay};
导出类组件{
只读仪表板$:可观察;
向量(…){
this.dashboard$=this.globalFiltersService.filters$.pipe(
//将筛选器事件映射到调用“GlobalFiltersService”getParams的结果`
map(=>this.globalFiltersService.getHttpParams()),
//将参数映射到一个新的“内部可观测”并展平结果。
//每当发生新事件时,`switchMap`将取消“内部可观测”
//由“可观测源”发出
switchMap(params=>this.http.get(`${environment.apiBase}network/dashboard`,{params})),
//避免在注册新订户时重新触发HTTP请求
//通过共享此流的最后一个值
共享重播(1)
);
}
}

问得好!我必须同步URL参数、表单和查询结果。这让我从建筑兔子洞一直到国家管理

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

以下是我作为示例构建的服务

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

  • 共享所有选项的状态(从表单组件/URL接收)
  • 使用URL参数同步选项
  • 使用选项同步所有表单
  • 查询结果
  • 分享结果
  • 要点如下:

    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 = {};
    

    现在你有了状态管理,而没有引入像redux这样的大型框架。

    啊,非常感谢——这正是我所需要的。我从这里稍微调整了一下解决方案(稍后将在问题中发布),但这让我找到了我需要去的地方。我最终不需要第一个map()操作符,因为filters$本身返回getHttpParams()的结果,我必须在共享重播之后添加一个take(1)操作符来让它玩球(我不完全理解为什么),但它工作得很好。更新:所以现在我想在其他两个观测值中的任何一个发出新值时更新我的仪表板。我不知道如何扩展这个解决方案来应对这个问题。如果您有任何建议,我们将不胜感激。谢谢Ben-这实际上与我在创建的GlobalFilters服务中已经实现的非常接近。我真的很想避免使用redux——这在一个更大的应用程序中是一个相当小的要求,我得到的印象是,redux最近倾向于在很多不需要它的角度应用程序中使用(至少我已经读到了这一点)。简单的可观察性提供了一些非常酷的状态管理ca
    // 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();
    }
    
    this._currentOptions.next(this._options);