Угловая виртуальная прокрутка: добавление новых элементов при достижении конца прокрутки
Я хотел бы использовать виртуальную прокрутку в моем приложении Angular. Элементы в моем списке являются результатом удаленного поиска. Я хотел бы загружать больше результатов (вызывать следующую страницу) каждый раз, когда достигаю конца прокрутки вниз по области просмотра.
Вот мой шаблон:
<div class="container">
<cdk-virtual-scroll-viewport itemSize="100">
<li *cdkVirtualFor="let item of searchResult">{{item}}</li>
</cdk-virtual-scroll-viewport>
</div>
Вот мои попытки заставить его работать так, как мне нужно:
export class SearchComponent {
@ViewChild(CdkVirtualScrollViewport) virtualScroll: CdkVirtualScrollViewport;
searchPageNumber: number;
searchResults: Array<any>;
constructor(private scrollDispatcher: ScrollDispatcher, private searchService: SearchService) {
this.searchPageNumber = 0;
this.searchResults = [];
}
ngOnInit(): void {
this.nextSearchPage(this.searchPageNumber);
}
ngAfterViewInit(): void {
//this.scrollDispatcher.register(this.scrollable);
//this.scrollDispatcher.scrolled(1000)
// .subscribe((viewport: CdkVirtualScrollViewport) => {
// console.log('scroll triggered', viewport);
// });
this.virtualScroll.renderedRangeStream.subscribe(range => {
console.log('range', range);
console.log('range2', this.virtualScroll.getRenderedRange());
if (this.virtualScroll.getRenderedRange().end % 10 === 0) {
this.nextSearchPage(++this.searchPageNumber);
}
});
}
nextSearchPage(pageNumber: number): void {
this.searchService.getResults(pageNumber).then((pagedResults) => {
this.searchResults = this.searchResults.concat(pageResuls);
});
}
}
Как вы можете видеть, я не могу понять, как вызвать звонок на мой nextSearchPage
Функция (для загрузки большего количества результатов) только тогда, когда полоса прокрутки достигает конца текущих отображаемых элементов в списке.
Вы знаете, как я могу это сделать?
Документация по угловой прокрутке плохая и в ней нет примеров, как указано в этом выпуске.
1 ответ
В CdkScrollable есть функция measureScrollOffset, которая может помочь. Вы можете подписаться на события прокрутки и проверить смещение, чтобы определить, где вы прокручивали.
ngAfterViewInit(): void {
this.scrollDispatcher.scrolled().pipe(
filter(event => this.virtualScroll.measureScrollOffset('bottom') === 0)
).subscribe(event => {
this.searchPageNumber++;
this.nextSearchPage(this.searchPageNumber);
})
И вам нужно вручную вызывать detectChange после изменения коллекции элементов прокрутки. Есть одна проблема, хотя я не нашел решения: полоса прокрутки вернется к началу после обнаружения изменений.
Проверьте демо здесь: https://stackblitz.com/edit/template-angular7-material-primeng-ckxgzs
Редактировать:
Оказывается, прослушивание конца прокрутки, вероятно, не очень хорошая идея. Лучше прислушиваться к диапазону рендеринга (я полагаю, что это фактические элементы, отображаемые в DOM). Когда диапазон рендеринга равен длине данных, самое время добавить дополнительные данные в коллекцию. И это как-то решает проблему возврата бара к началу.
ngAfterViewInit(): void {
this.scrollDispatcher.scrolled().pipe(
filter(event => this.virtualScroll.getRenderedRange().end === this.virtualScroll.getDataLength())
).subscribe(event => {
this.searchPageNumber++;
this.nextSearchPage(this.searchPageNumber);
})
Новая демонстрация здесь: https://stackblitz.com/edit/template-angular7-material-primeng-fhikcf
Честно говоря, я получил идею от: https://angularfirebase.com/lessons/infinite-virtual-scroll-angular-cdk/ Вы, вероятно, можете узнать больше там.
Самый простой способ решить эту проблему - использовать scrolledIndexChange.
<cdk-virtual-scroll-viewport itemSize="500" (scrolledIndexChange)="nextBatch($event)">
$event - это первый индекс отображаемых элементов на данный момент. Следовательно, вы можете вызвать следующую страницу, когда индекс первого элемента находится рядом с концом элементов в списке.
Помните, что элементы имеют фиксированную высоту (itemSize), поэтому легко определить количество элементов, отображаемых за раз.
например
nextBatch(index) {
if (index + $itemsRenderAtTheMoment === this.items.length - 1) {
this.page ++;
this.loadMore();
}
}