Angular virtual scroll: append new items when reaching the end of scroll

馋奶兔 提交于 2020-12-29 09:17:04

问题


I would like to use virtual scroll on my Angular application. The items in my list are the result of a remote paged search. I would like to load more results (call the next page) every time I reach the end of the viewport scrolling down.

Here is my template:

<div class="container">
    <cdk-virtual-scroll-viewport itemSize="100">
        <li *cdkVirtualFor="let item of searchResult">{{item}}</li>
    </cdk-virtual-scroll-viewport>
</div>

Here is my attempts to make it to work as I need:

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);
        });
    }
}

As you can see I can't figure out how to trigger the call to my nextSearchPage function (to load more results) only when the scrollbar reaches the end of the current rendered items in the list.

Do you know how can I do that?

Angular scrolling documentation is poor and it lacks of examples, as stated in this issue as well.


回答1:


There is a function measureScrollOffset on CdkScrollable that may help. you can subscribe to scroll events and check the offset to determine where you have scrolled.

  ngAfterViewInit(): void {
    this.scrollDispatcher.scrolled().pipe(
      filter(event => this.virtualScroll.measureScrollOffset('bottom') === 0)
    ).subscribe(event => {
      this.searchPageNumber++;
      this.nextSearchPage(this.searchPageNumber); 
    })

And you need to manually call detectChange after scroll item collection change. There's one problem though I have not find a solution: the scroll bar will return to beginning after change detection.

Check the demo here: https://stackblitz.com/edit/template-angular7-material-primeng-ckxgzs

Edit:

Turns out listening for end of scroll is probably not a good idea. A better one is listening for rendering range(I believe it's the actual items rendered in the DOM) change. Once the rendering range ends equals data length, that's the time to add more data to the collection. And this somehow solves the problem of bar going back to beginning.

 ngAfterViewInit(): void {
    this.scrollDispatcher.scrolled().pipe(
      filter(event => this.virtualScroll.getRenderedRange().end === this.virtualScroll.getDataLength())
    ).subscribe(event => {
      this.searchPageNumber++;
      this.nextSearchPage(this.searchPageNumber);
    })

New demo here: https://stackblitz.com/edit/template-angular7-material-primeng-fhikcf

To be honest, I got the idea from: https://angularfirebase.com/lessons/infinite-virtual-scroll-angular-cdk/ You probably can learn more there.




回答2:


The easiest way to solve this is with the scrolledIndexChange.

<cdk-virtual-scroll-viewport itemSize="500" (scrolledIndexChange)="nextBatch($event)">

$event is the first index of rendered elements at the moment. Therefore, you can call the next page when the index of the first item is near the end of the items in the list

Remember that the items have a fixed height (itemSize) so it is easy to identify the amount of elements that are rendered at a time.

e.g.

  nextBatch(index) {
    if (index + $itemsRenderAtTheMoment === this.items.length - 1) {
      this.page ++;
      this.loadMore();
    }
  }


来源:https://stackoverflow.com/questions/54770671/angular-virtual-scroll-append-new-items-when-reaching-the-end-of-scroll

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!