I find different samples for the VirtualScrollViewPort but with all I have the problem how to use them in real life. The samples load the whole data at once from the server - but since they are too large, I wanna load them individually. My main requirements are:
I tried already the approach from Specifying data
section - but there I fail how to recognize that no data is loaded AND I fail to initiate the view - especially when the user resets the content.
I tried as well with
<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" >
which works for the first requirements but finally I fail with scrolledIndexChange
is fired only on the very first item on the list. I have no clue how to track if user already is displaying already item 6 (which would load additional data). On the API page I do not see any @Output()
beside the scrolledIndexChange
.
Any hint how to track the events properly?
UPDATE
First problem I figured out was the incorrect syntax of setting the height, i.e. [itemSize]="itemHeight"
is the appropriate syntax otherwise it remains always to zero ==> all elements are rendered!
After some work my final solution looks like:
<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">
where it is noteworthy that my list is an async list, named lstSearchResults
and in the the ts
code I have:
// 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);
});
}
and for the scrolling I have the following code:
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;
}
}