I am trying to load 20 items through the virtual scroll component, but I see 34 as soon as the page loads, without doing any scrolling at all. Have I got the css wrong? Or there is some setting on the control that I forgot? The project is located here.
2 Answers
This tutorial on Firebase says:
Let’s start by reviewing a few important concepts with virtual scroll. First, you declare the cdk-virtual-scroll-viewport component to provide a context for virtual scrolling. It should have an itemSize input property defined as the pixel height of each item.
You can see that this is not made especially for grid scroll views, but we can work around this limitation:
You have 5 columns of images each 160 pixels high plus margins top and bottom each 10 pixels, yielding 180 pixels.
Now we calculate 180 divided by 5 and get 36. This is the itemSize
you need.
<cdk-virtual-scroll-viewport itemSize="36">
<div class="image" *cdkVirtualFor="let image of images">
<a (click)="viewDetail(image)">
<img src="https://picsum.photos/200/160/?image={{ image.id }}" />
</a>
</div>
</cdk-virtual-scroll-viewport>
See this fork of your StackBlitz
The official documentation is here: Angular | Scrolling
You could change the buffer sizes as well
<cdk-virtual-scroll-viewport itemSize="36" minBufferPx="540" maxBufferPx="540">
<!-- ... -->
</cdk-virtual-scroll-viewport>
See: Angular | Scrolling # Scrolling over fixed size items
You can also let Angular do the math:
<cdk-virtual-scroll-viewport itemSize="{{ 180 / 5 }}" minBufferPx="{{ 180 * 3 }}" maxBufferPx="{{ 180 * 3 * 3 }}">
That means that you will load three pages (3 rows per page) in advance and load more if only one page buffer (3 rows of images per page) is left

- 32,854
- 11
- 73
- 106
-
To make the min and max buffer always take the full height of the page, I used this in the virtual scroller `minBufferPx="{{ window.screen.height * 1.2 }}" maxBufferPx="{{window.screen.height * 2}}"` (You also need to define `public window = window;` in the page component). This lets the min buffer be 1.2x the size of the page, and at most render 2x the amount of screen space you have. – Cooper Scott Mar 22 '23 at 14:13
One Idea is to load only a couple of items and using an Observer to load next bunch of items
<cdk-virtual-scroll-viewport
itemSize="10"
minBufferPx="1200"
maxBufferPx="1200"
(scrolledIndexChange)="getNextBatch($event)">
...
</cdk-virtual-scroll-viewport>
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { ImageService } from '../image.service';
import { Subscription } from 'rxjs';
import { Router } from '@angular/router';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
@Component({
selector: 'app-gallery',
templateUrl: './gallery.component.html',
styleUrls: ['./gallery.component.css'],
})
export class GalleryComponent implements OnInit, OnDestroy {
@ViewChild(CdkVirtualScrollViewport) viewport: CdkVirtualScrollViewport;
constructor(public service: ImageService, private router: Router) {}
...
getNextBatch(e) {
const end = this.viewport.getRenderedRange().end;
const total = this.viewport.getDataLength();
console.log(end)
console.log(total)
if (end === total) {
console.log('request next batch');
}
}
}

- 829
- 1
- 11
- 21