使用 RxJS 如何通过另一个动作流触发 observable [英] With RxJS how to trigger observables by another action stream

查看:31
本文介绍了使用 RxJS 如何通过另一个动作流触发 observable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我试图通过 angular 和 RxJS 反应性地构建一个列表组件.该列表具有许多功能,例如:

So I am trying to build a list component reactively by angular and RxJS. the list has a lot of features like:

  • 按词搜索
  • 更改页码
  • 更改页面大小
  • 按字段过滤
  • 排序

搜索服务代码:

private seachItemSubject = new BehaviorSubject<string>("");
private pageLimitSubject = new BehaviorSubject<number>(10);
private pageNumberSubject = new BehaviorSubject<number>(0);
private orderingSubject = new BehaviorSubject<ListOrder>({});
private filteringSubject = new BehaviorSubject<any>({});

searchItemAction$ = this.seachItemSubject.asObservable();
pageLimitAction$ = this.pageLimitSubject.asObservable();
pageNumberAction$ = this.pageNumberSubject.asObservable();
orderingAction$ = this.orderingSubject.asObservable();
filteringAction$ = this.filteringSubject.asObservable();

combination$ = Observable.combineLatest(
    this.searchItemAction$,
    this.pageLimitAction$,
    this.pageNumberAction$,
    this.orderingAction$,
    this.filteringAction$
)

changeSeachItem(searchItem: string) {
    this.seachItemSubject.next(searchItem);
}

changePageLimit(pageLimit: number) {
    this.pageLimitSubject.next(pageLimit);
}

changePageNumber(pageNumber: number) {
    this.pageNumberSubject.next(pageNumber);
}

changeOrdering(listOreder: ListOrder) {
    this.orderingSubject.next(listOreder);
}

changeFilters(filters: any) {
    this.filteringSubject.next(filters)
}

组件代码

results$: Observable<MyResult> = this.listSearchService.combination$
    .switchMap(([filterItem, pageLimit, pageNumber, ordering, filters]) => this.listService.query({
        offset: pageNumber,
        limit: pageLimit,
        filter: filterItem,
        order: ordering.order,
        descending: ordering.descending,
        ...filters
    }));

我现在面临的问题是,如果用户使用过滤功能,我希望更改页码.

the problem that I am facing now is that I want the page number to be changed if the user used the filtering feature.

我想我想要的是某种主题或事件,只要用户使用过滤功能就可以调用它们.

I think what I want is some sort of an Subject or an event that can be called whenever the user uses the filtering feature.

所以它会是这样的.

applyFilters(filters: any) {
    this.listSearchService.changePageNumber(0);
    this.listSearchService.changeFilters(filters);
    this.listSearchService.commit.next() -----> so it would change both then start the combine latest
    console.log(filters)
}

我不确定这是否是正确答案,所以如果您有更好的答案,请告诉我.

I am not sure that that would be the right answer, so please tell me if you have a better answer.

谢谢

推荐答案

正如您所发现的,您不能使用下面的原始解决方案,因为 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$
)

这篇关于使用 RxJS 如何通过另一个动作流触发 observable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆