且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用 RxJS 如何通过另一个动作流触发 observable

更新时间:2021-07-22 08:48:07

正如您所发现的,您不能使用下面的原始解决方案,因为 combineLatest 将在 changePageNumber 时同时触发和 changeFilters 是从 applyFilters 调用的.

As you have discovered, you cannot use the original solution below as combineLatest will fire both when changePageNumber and changeFilters are called from applyFilters.

另一种方法是使用通过 scan 运算符实现的单个状态对象,并一次更新其中的一个或多个属性.这意味着从它触发的服务只会被每个动作"调用一次.

An alternative approach is to use a single state object implemented via the scan operator, and update one or more properties in it at a time. This means that the service firing from it will only be called once per 'action'.

顺便说一句,您当前正在每个 BehaviorSubject 中存储您的状态",并将其组合起来.

BTW you are currently storing your 'state' in each BehaviorSubject, and combining it.

class ListState {
  searchItem: any = "";
  pageLimit: any = 10;
  pageNumber: any = 0;
  ordering: any = "asc";
  filtering: any = "";
}

const listStateUpdatesSubject = new BehaviorSubject<Partial<ListState>>(null);
const listStateUpdates$ = listStateUpdatesSubject.asObservable();

// fires once per state update, which can consist of multiple properties
const currListState$ = listStateUpdates$.pipe(
  filter(update => !!update),
  scan<Partial<ListState>, ListState>(
    (acc, value) => ({ ...acc, ...value }), new ListState)
);

const callListService$ = currListState$.pipe(map(state => listService(state)));

function changeOrdering(listOreder: string) {
    changeState({ ordering: listOreder });
}

function changeFilters(filters: any) {
    changeState({ pageNumber: 0, filtering: filters }); // set both page number and filtering
}

// etc

这是一个工作演示:https://stackblitz.com/edit/so-rxjs-filtering-2?file=index.ts,请注意对 changeFilters 的调用会导致服务仅被调用一次.

Here is a working demo: https://stackblitz.com/edit/so-rxjs-filtering-2?file=index.ts, note that a call to changeFilters results in the service being called once only.

原始解决方案

创建一个监听 filteringAction$ 的新 observable,执行所需的操作并在 combineLatest 中使用新的 observable.

Create a new observable that listens to filteringAction$, perform the desired actions and use that new observable within the combineLatest.

...
orderingAction$ = this.orderingSubject.asObservable();
filteringAction$ = this.filteringSubject.asObservable();

filterApplied$ = filteringAction$.pipe(map(filter => applyFilters(filter)));

applyFilters(filters: any) {
  this.listSearchService.changePageNumber(0);
  this.listSearchService.changeFilters(filters);
  return filters;
}

combination$ = Observable.combineLatest(
  this.searchItemAction$,
  this.pageLimitAction$,
  this.pageNumberAction$,
  this.orderingAction$,
  this.filterApplied$    // instead of filteringAction$
)