Угловая виртуальная прокрутка: добавление новых элементов при достижении конца прокрутки

Я хотел бы использовать виртуальную прокрутку в моем приложении 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();
    }
  }
Другие вопросы по тегам