使用观测值进行简单的无限滚动分页

时间:2019-05-09 23:45:03

标签: angular rxjs

在我的应用程序中,有很多利用服务器端分页和搜索的表。我要使这些表使用无限滚动,并且在表底部使用“加载更多”按钮。我想创建一个可以在所有这些表的所有视图中重复使用的服务,但是我在rxjs和Observables上还很陌生,似乎找不到正确的方法来完成我需要的一切。这是我到目前为止的内容:

可观察的搜索流,每当给出唯一的搜索词时,它将返回一组新的QueryOptions

// Setup our search stream that will trigger the $resources observable to be 
// refreshed upon search updating
this.searchStream = this.searchWatcher.pipe(
    // wait Xms after each keystroke before considering the term
    debounceTime(this.searchDebounceTime),

    // ignore new term if same as previous term
    distinctUntilChanged(),

    // switch to new search observable each time the term changes
    map((term: string) => {
        this.searchTerm = term;
        this.pageCurrent = 0;
        return new QueryOptions(this.pageSize, this.pageSize * this.pageCurrent, this.searchTerm);
    })
);

可分页的可观察流,每次我们转到另一页时(由于其无限滚动,其始终为QueryOptions),该流还将返回一组新的this.pageCurrent++。显然,我们将在所有分页请求中保留searchTerm

// Setup our page stream that will trigger the $resources observable to be
// refreshed upon the page being changed
this.pageStream = this.pageWatcher.pipe(
    map(page => {
        return new QueryOptions(this.pageSize, this.pageSize * page, this.searchTerm);
    })
);

现在,我需要将这两个流合并为一个optionsStream,并告诉它从第1页开始,并且没有这样的搜索字词:

// This becomes a stream of the most recently updated options
let optionsStream = merge(this.searchStream, this.pageStream).pipe(
    // this line initializes the page at page 1, with `null` as the search term
    startWith(new QueryOptions(this.pageSize, 0))
);

最后是我的dataStream,它只接受上游提供的选项并向后端服务器发出请求:

// This stream is the data stream triggered when the options are updated
this.dataStream = optionsStream.pipe(
    mergeMap(options => {
        // this.source(...) will return an observable of the items
        // I want to page through. e.g. returns a `Obserable<any[]>`
        return this.source(options);
    })
);

到目前为止,这一切都非常有效,当这两个流中的任何一个对选项进行更改时,我都会得到正确的更新。现在我的问题一直在试图找出如何做两件事:

  1. 我需要累积来自dataStream的所有事件,以便如果第1页有10个项目,那么在转到第2页时,现在应该有20个项目,然后第3页将有30个项目物品等。我想我可以使用scan来做到这一点,甚至编写了一些小测试方案来累积“页面”,如下所示:
let page1 = [1, 2, 3];
let page2 = [4, 5, 6];
let page3 = [7, 8, 9];

let pager = new BehaviorSubject([]);
let pagerObs = pager.asObservable();

pagerObs.pipe(
    scan((acc, val) => {
        return acc.concat(val);
    })
).subscribe(page => {
    console.log(page);
});

setTimeout(() => {
    pager.next(page1);
}, 2000);

setTimeout(() => {
    pager.next(page2);
}, 4000);

setTimeout(() => {
    pager.next(page3);
}, 6000);

// output looks like [1,2,3], then [1,2,3,4,5,6], then [1,2,3,4,5,6,7,8,9]
// which is exactly what I want!
  1. 使用新的搜索词时,我需要“重置”可观察对象,以便scan以空集[]重新开始,但是我无法找出正确的方法来完成此操作使用纯可观察物。

有人可以在这里填补我如何使用可观察对象实现这种功能的空白吗?

TL; DR

在rxjs中使用scan运算符来浏览具有许多对象的后端服务器时,鉴于有人更新了搜索词,因此“重置”累加器并从头开始的最佳方法是什么?从第1页开始。

更新

好吧,我想我已经弄明白了,但是我感觉这有点hacky,没有按照预期的方式利用可观察到的运算符:

如果我使用数据流并使用scan,则在页面重置回val的情况下,我可以扩展管道以仅返回0。乍一看似乎可行,但是我将不得不继续对其进行测试。

对于任何有更优雅的解决方案或建议的人来说,问题都会公开

this.$resources = this.dataStream.pipe(
    scan((acc, val) => {
        return this.pageCurrent == 0 ? val : acc.concat(val);
    }),
    share(),
    tap(_ => {
        this.isLoading = false;
        this.isSearching = false;
    })
);

0 个答案:

没有答案