在我的应用程序中,有很多利用服务器端分页和搜索的表。我要使这些表使用无限滚动,并且在表底部使用“加载更多”按钮。我想创建一个可以在所有这些表的所有视图中重复使用的服务,但是我在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);
})
);
到目前为止,这一切都非常有效,当这两个流中的任何一个对选项进行更改时,我都会得到正确的更新。现在我的问题一直在试图找出如何做两件事:
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!
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;
})
);