使用RxJS如何触发另一个动作流的可观察对象 [英] With RxJS how to trigger observables by another action stream

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

问题描述

因此,我试图通过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 $ ,执行所需的操作,并在 combineLatest 中使用该新的可观察对象.

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如何触发另一个动作流的可观察对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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