Observable<Object[]> - 我可以skip/take吗?
Observable<Object[]> - Can I skip/take?
我想无限滚动我的对象列表,它们是 Observable<Object[]>
。将它们转移到 promise 并 await
ing 它们不是一种选择,因为它们需要实时更新。
我所做的是使用 .map()
哪种方法有效,但问题是 angular 每当我使用 20
项目而不是 10
例如。
一个解决方案是首先实际跳过 10
并加载下一个 10
,这只会向页面添加新元素。但是我不确定这是否可能?
这是我的
打字稿
// Every time this happens page goes to top
// because all the items are re-rendered, not only new ones
public onScroll(): void {
this.take += 10; // on ngOnInit() take is set to 10.
this.sales = this.filteringObservable.map(data => {
return _.take(data, this.take);
});
}
HTML
<div class="sales-container" infiniteScroll [infiniteScrollDistance]="2" [infiniteScrollThrottle]="50" (scrolled)="this.onScroll()">
<app-sales-item *ngFor="let sale of this.sales | async" [sale]="sale"></app-sales-item>
</div>
这是我在这个场景中使用的一些代码:
private filterModel: FilterModel = new FilterModel();
private page: number = 0;
items: ResultModel[] = [];
public onScroll(): void {
if (this.scrolling) {
return;
}
this.scrolling = true;
this.page++;
this.service.loadStuff(this.filterModel, this.page).subscribe(items => {
!items || !items.length
? this.page--
: items.forEach(item => this.items.push(item));
this.scrolling = false;
},
() => this.scrolling = false);
}
其中 filterModel
是一个对象,其中包含有关加载内容和加载方式的数据 - 页面大小、属性 过滤器等。
如果你想以更 RxJs 的方式完成这个,你可以尝试这样的事情(未测试,根据你的需要调整):
private filterModel: FilterModel = new FilterModel();
private filter$ = new Subject<FilterModel>();
private page: number = 0;
private page$ = new Subject<number>();
private cache: ResultModel[][] = [];
items$: Observable<ResultModel>;
ngOnInit() {
const filter$ = this.filter$.pipe(
debounceTime(1000),
distinctUntilChanged(),
tap(() => this.cache = []),
map(model => ({ model: this.filterModel = model, page: 0 }))
);
const page$ = this.page$.pipe(
map(pageNumber => ({ model: this.filterModel, page: pageNumber }))
);
this.items$ = merge(page$, filter$).pipe(
startWith({ model: this.filterModel, page: this.page }),
tap(() => this.scrolling = true)
mergeMap(({ model: FilterModel, page: number }) =>
this.service.loadStuff(model, page).pipe(
tap(response => this.cache[page] = response.items)
)
),
tap(response => ... ), // Do whatever else with your response here
map(() => _.flatMap(this.cache)),
tap(() => this.scrolling = false)
);
}
onScroll() {
this.page$.next(this.page++);
}
onFilter(model: FilterModel){
this.filter$.next(model);
}
希望这能给您带来正确的方向:)
这很容易实现,我有多个这样的列表。
首先你想使用某种库,我使用
ngx-infinite-scroll.
那么您希望您的列表使用它:
<ul infiniteScroll (scrolled)="getItems()">
<li *ngFor="let item of items; trackBy:trackByFn">{{item}}</li>
</ul>
trackByFn(i: number, item) {
return item.<unique-key>;
}
下一步你想对滚动做出反应,通过 scrolled
侦听器执行此操作,如上所示。
现在,要使无限列表正常工作,您需要的是每个项目都有一个唯一的键。您还需要列表可用项目的总数,以及当前显示的项目数。
这是我为此使用的函数,如果您不使用 API 来获取更多结果,您可以根据自己的喜好对其进行调整。
// isReset decides whether to set offset to 0 or not
getItems(isReset = false): void {
if (!isReset && this.items.length >= this.total) {
return;
}
this.api.get<CustomResponse>('my-api-endpoint', {
limit: 10,
offset: isReset ? 0 : this.items.length,
})
.pipe(
first(),
tap((res) => this.total = res.totalsize || 0),
map((res) => res.list)
)
.subscribe((items) => {
// This bit prevents the same batch to be loaded into the list twice if you scroll too fast
if (this.items.length && !isReset) {
if (items[items.length - 1].<unique-key> === this.items[this.items.length - 1].<unique-key>) {
return;
}
}
this.items = isReset ? items : [...this.items, ...items];
})
);
}
确保将 <unique-key>
替换为每个项目的唯一键。
希望对您有所帮助。
这就是我们使用 Angular 5 和 WebApi 作为服务器端的方式。我们已经在我们的表格中实现了某种排序,但您可以只使用部分滚动并在您的列表中使用它。此外,我们从表中获取所有数据,然后以 10 为单位将其发送给客户端,但如果您需要速度,您可以在 SQL 服务器中对数据进行分页,一次只获取 10 行。如果您需要这种寻呼的逻辑,请告诉我。
HTML
<div #scb id="scb" class="scrollBarClass" *ngIf="sales && sales.length" (scroll)="onScroll()">
<div class="table table-striped table-responsive table-sm table-bordered">
<table class="table" *ngIf="sales && sales.length">
// here goes you table structure, headers, body and so...
</table>
</div>
</div>
CSS
.scrollBarClass {
max-height: 500px;
overflow-y: scroll;
}
Angular分量
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
@ViewChild('scb') grid: ElementRef;
scroll = false;
page = 1;
total = 0;
onScroll() {
if (this.grid.nativeElement.scrollHeight - this.grid.nativeElement.scrollTop < 510 && this.scroll == false) {
if (this.sales.length == this.total) {
return;
}
let p: any;
this.page++;
this.scroll = true;
this._myService.searchSales(this.page, 10, this.sortType, this.sortAD)
.subscribe(
(data) => { p = data['data']; this.total = data['count']},
error => { this.errorMessage = <any>error; this.page--; },
() => {
this.scroll = false;
Array.prototype.push.apply(this.sales, p);
});
}
}
Angular 服务
searchSales(page: number, recperpage: number, sorttype: string, sortad: string) {
let params = new HttpParams()
.append('page', page.toString())
.append('recperpage', recperpage.toString())
.append('sorttype', sorttype)
.append('sortad', sortad);
return this._http.get<any[]>('sales/searchsales', { params: params });
}
WebAPI 控制器
[HttpGet]
public IHttpActionResult searchsales(int page, int recperpage, string sorttype, string sortad)
{
try
{
var count = 0;
var r = _salesService.SearchSales(sorttype, sortad, ref count);
return Ok(new { data=r.Skip((page - 1) * recperpage).Take(recperpage).ToList(), count });
}
catch (Exception e)
{
return InternalServerError(e);
}
}
WebAPI 服务
public List<Sales> SearchSales(string sorttype, string sortad, ref int count)
{
var query = "";
query = " select * FROM Sales "+
" ORDER BY " + ((sorttype == null) ? "DateOfSale" : sorttype) + " " + ((sortad == null) ? "DESC" : sortad);
var result = SQLHelper.ExecuteReader(query);
count = result.Count;
return result;
}
我想无限滚动我的对象列表,它们是 Observable<Object[]>
。将它们转移到 promise 并 await
ing 它们不是一种选择,因为它们需要实时更新。
我所做的是使用 .map()
哪种方法有效,但问题是 angular 每当我使用 20
项目而不是 10
例如。
一个解决方案是首先实际跳过 10
并加载下一个 10
,这只会向页面添加新元素。但是我不确定这是否可能?
这是我的
打字稿
// Every time this happens page goes to top
// because all the items are re-rendered, not only new ones
public onScroll(): void {
this.take += 10; // on ngOnInit() take is set to 10.
this.sales = this.filteringObservable.map(data => {
return _.take(data, this.take);
});
}
HTML
<div class="sales-container" infiniteScroll [infiniteScrollDistance]="2" [infiniteScrollThrottle]="50" (scrolled)="this.onScroll()">
<app-sales-item *ngFor="let sale of this.sales | async" [sale]="sale"></app-sales-item>
</div>
这是我在这个场景中使用的一些代码:
private filterModel: FilterModel = new FilterModel();
private page: number = 0;
items: ResultModel[] = [];
public onScroll(): void {
if (this.scrolling) {
return;
}
this.scrolling = true;
this.page++;
this.service.loadStuff(this.filterModel, this.page).subscribe(items => {
!items || !items.length
? this.page--
: items.forEach(item => this.items.push(item));
this.scrolling = false;
},
() => this.scrolling = false);
}
其中 filterModel
是一个对象,其中包含有关加载内容和加载方式的数据 - 页面大小、属性 过滤器等。
如果你想以更 RxJs 的方式完成这个,你可以尝试这样的事情(未测试,根据你的需要调整):
private filterModel: FilterModel = new FilterModel();
private filter$ = new Subject<FilterModel>();
private page: number = 0;
private page$ = new Subject<number>();
private cache: ResultModel[][] = [];
items$: Observable<ResultModel>;
ngOnInit() {
const filter$ = this.filter$.pipe(
debounceTime(1000),
distinctUntilChanged(),
tap(() => this.cache = []),
map(model => ({ model: this.filterModel = model, page: 0 }))
);
const page$ = this.page$.pipe(
map(pageNumber => ({ model: this.filterModel, page: pageNumber }))
);
this.items$ = merge(page$, filter$).pipe(
startWith({ model: this.filterModel, page: this.page }),
tap(() => this.scrolling = true)
mergeMap(({ model: FilterModel, page: number }) =>
this.service.loadStuff(model, page).pipe(
tap(response => this.cache[page] = response.items)
)
),
tap(response => ... ), // Do whatever else with your response here
map(() => _.flatMap(this.cache)),
tap(() => this.scrolling = false)
);
}
onScroll() {
this.page$.next(this.page++);
}
onFilter(model: FilterModel){
this.filter$.next(model);
}
希望这能给您带来正确的方向:)
这很容易实现,我有多个这样的列表。
首先你想使用某种库,我使用 ngx-infinite-scroll.
那么您希望您的列表使用它:
<ul infiniteScroll (scrolled)="getItems()">
<li *ngFor="let item of items; trackBy:trackByFn">{{item}}</li>
</ul>
trackByFn(i: number, item) {
return item.<unique-key>;
}
下一步你想对滚动做出反应,通过 scrolled
侦听器执行此操作,如上所示。
现在,要使无限列表正常工作,您需要的是每个项目都有一个唯一的键。您还需要列表可用项目的总数,以及当前显示的项目数。
这是我为此使用的函数,如果您不使用 API 来获取更多结果,您可以根据自己的喜好对其进行调整。
// isReset decides whether to set offset to 0 or not
getItems(isReset = false): void {
if (!isReset && this.items.length >= this.total) {
return;
}
this.api.get<CustomResponse>('my-api-endpoint', {
limit: 10,
offset: isReset ? 0 : this.items.length,
})
.pipe(
first(),
tap((res) => this.total = res.totalsize || 0),
map((res) => res.list)
)
.subscribe((items) => {
// This bit prevents the same batch to be loaded into the list twice if you scroll too fast
if (this.items.length && !isReset) {
if (items[items.length - 1].<unique-key> === this.items[this.items.length - 1].<unique-key>) {
return;
}
}
this.items = isReset ? items : [...this.items, ...items];
})
);
}
确保将 <unique-key>
替换为每个项目的唯一键。
希望对您有所帮助。
这就是我们使用 Angular 5 和 WebApi 作为服务器端的方式。我们已经在我们的表格中实现了某种排序,但您可以只使用部分滚动并在您的列表中使用它。此外,我们从表中获取所有数据,然后以 10 为单位将其发送给客户端,但如果您需要速度,您可以在 SQL 服务器中对数据进行分页,一次只获取 10 行。如果您需要这种寻呼的逻辑,请告诉我。
HTML
<div #scb id="scb" class="scrollBarClass" *ngIf="sales && sales.length" (scroll)="onScroll()">
<div class="table table-striped table-responsive table-sm table-bordered">
<table class="table" *ngIf="sales && sales.length">
// here goes you table structure, headers, body and so...
</table>
</div>
</div>
CSS
.scrollBarClass {
max-height: 500px;
overflow-y: scroll;
}
Angular分量
import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
@ViewChild('scb') grid: ElementRef;
scroll = false;
page = 1;
total = 0;
onScroll() {
if (this.grid.nativeElement.scrollHeight - this.grid.nativeElement.scrollTop < 510 && this.scroll == false) {
if (this.sales.length == this.total) {
return;
}
let p: any;
this.page++;
this.scroll = true;
this._myService.searchSales(this.page, 10, this.sortType, this.sortAD)
.subscribe(
(data) => { p = data['data']; this.total = data['count']},
error => { this.errorMessage = <any>error; this.page--; },
() => {
this.scroll = false;
Array.prototype.push.apply(this.sales, p);
});
}
}
Angular 服务
searchSales(page: number, recperpage: number, sorttype: string, sortad: string) {
let params = new HttpParams()
.append('page', page.toString())
.append('recperpage', recperpage.toString())
.append('sorttype', sorttype)
.append('sortad', sortad);
return this._http.get<any[]>('sales/searchsales', { params: params });
}
WebAPI 控制器
[HttpGet]
public IHttpActionResult searchsales(int page, int recperpage, string sorttype, string sortad)
{
try
{
var count = 0;
var r = _salesService.SearchSales(sorttype, sortad, ref count);
return Ok(new { data=r.Skip((page - 1) * recperpage).Take(recperpage).ToList(), count });
}
catch (Exception e)
{
return InternalServerError(e);
}
}
WebAPI 服务
public List<Sales> SearchSales(string sorttype, string sortad, ref int count)
{
var query = "";
query = " select * FROM Sales "+
" ORDER BY " + ((sorttype == null) ? "DateOfSale" : sorttype) + " " + ((sortad == null) ? "DESC" : sortad);
var result = SQLHelper.ExecuteReader(query);
count = result.Count;
return result;
}