当另一个可观察对象发生变化时,我如何 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。
这是我的要求。 (直接适用于问题的)
- 共享所有选项的状态(从表单 components/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 = {};
然后使用 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 这样庞大的框架就可以进行状态管理了。
大局,我想要实现的是一组过滤器我的页面 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。
这是我的要求。 (直接适用于问题的)
- 共享所有选项的状态(从表单 components/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 = {};
然后使用 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 这样庞大的框架就可以进行状态管理了。