Angular Material VirtualScrollViewPort - 异步加载数据

Angular Material VirtualScrollViewPort - load data async

我为 VirtualScrollViewPort 找到了不同的样本,但我仍然不知道如何在现实生活中使用它们。样本一次从服务器加载全部数据 - 但由于它们太大,我想单独加载它们。我的主要要求是:

  1. 搜索掩码 - 用户输入一些数据
  2. 显示进度条...
  3. 在服务器上查询搜索
  4. 如果没有找到结果 ==> 显示一条消息
  5. 如果找到东西 ==> 显示前 n (=10) 个项目
  6. 用户向下滚动后,仅例如还剩 5 件 ==> 额外加载 10 件
    • 继续 5。
    • 如果只是例如还剩 3 ==> 结束滚动

我已经尝试了 Specifying data 部分的方法 - 但我无法识别没有加载数据并且我无法启动视图 - 特别是当用户重置内容时。

我也试过

<cdk-virtual-scroll-viewport itemSize="itemHeight" (scrolledIndexChange)="nextBatch($event,(resultList[resultList.length-1].total) )"
class="scroll-container">
  <div *cdkVirtualFor="let search of resultList"  class="card-item" >

适用于第一个要求,但最终我失败了 scrolledIndexChange 仅在列表中的第一项上被触发。我不知道如何跟踪用户是否已经在显示第 6 项(这将加载其他数据)。在 API page 上,我在 scrolledIndexChange 旁边没有看到任何 @Output()

有什么提示可以正确跟踪事件吗?

更新 我发现的第一个问题是设置高度的语法不正确,即 [itemSize]="itemHeight" 是合适的语法,否则它始终保持为零 ==> 所有元素都被渲染!

经过一些工作后,我的最终解决方案如下所示:

<ng-container *ngIf="lstSearchResults|async as resultList; else searching">
    <cdk-virtual-scroll-viewport [itemSize]="itemHeight" (scrolledIndexChange)="nextBatch()"
         class="scroll-container">
       <div *cdkVirtualFor="let search of resultList"  class="card-item">

值得注意的是我的列表是一个异步列表,名为 lstSearchResults 并且在 ts 代码中我有:

// for proper height and caching... (in pixels)
itemHeight = 174;

search(searchConfig: SearchOptions): void {
    //    ....
    this.lstSearchResults = new BehaviorSubject<SearchResult[]>(null);
    // call the REST service
    this.searchService.doSearch(searchConfig).subscribe((foundEntry) => {
        if (!this.resultList) {
            // first list - nothing found up till now
            this.resultList = foundEntry;
        } else {
            if (!this.resultList[this.resultList.length - 1]) {
                //remove the marker (which was added below/previously)
                this.resultList.pop();
            }
            foundEntry.map((item) => this.resultList.push(item));
        }
        if (this.resultList[0] && this.resultList[0].total > this.resultList.length + 1) {
            //some more elements could be fetched from the server ==> add a dummy entry for rendering
            this.resultList.push(undefined);
        }
        // notify the search list to be updated
        this.lstSearchResults.next(this.resultList);
    });
}

对于滚动,我有以下代码:

nextBatch(): void {
    if (this.theEnd) {
        return;
    }

    if (this.resultList[0]) {
        // something was found
        if (
            this.viewport.getRenderedRange().end ===
            this.viewport.getDataLength()
        ) {
            // since we scrolled to the very end of the rendered display
            // ==> check if further search is required (and do so...)
            const searchTotal = this.resultList[0].total;
            this.mySearchConfig.posOffset += this.mySearchConfig.noOfElements;
            // some basic check if the total counter exceeds the current offset
            // i.e. no further search required
            if (this.mySearchConfig.posOffset <= searchTotal) {
                this.search(this.mySearchConfig, true);
            } else {
                this.theEnd = true;
            }
        }
    } else {
        // nothing found ==> mark the end
        this.theEnd = true;
    }
}