I have encountered a challenge while trying to implement infinite virtual scroll on an Angular 7 project with a GraphQL backend from Hasura.
I am puzzled by the fact that the new data is not being added and there are multiple API requests being made when I scroll.
Below is the code snippet for the feed.component.ts component:
import { Component, OnInit, ViewChild } from '@angular/core';
import { IdeaService } from '@app/core/idea/idea.service';
import { MatDialog } from '@angular/material';
import { IdeaCardComponent } from '@app/shared/idea-card/idea-card.component';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Observable, BehaviorSubject } from 'rxjs';
import { map, tap, merge, throttleTime, scan, mergeMap } from 'rxjs/operators';
const batchSize = 10;
@Component({
selector: 'app-idea-feed',
templateUrl: './idea-feed.component.html',
styleUrls: ['./idea-feed.component.scss']
})
export class IdeaFeedComponent implements OnInit {
@ViewChild(CdkVirtualScrollViewport)
viewport: CdkVirtualScrollViewport;
isEndOfTheList = false;
offset = new BehaviorSubject(null);
infinite: Observable<any[]>;
length: number;
ideaList: any[] = [];
pageIndex = 0;
pageEvent: any;
constructor(private ideaService: IdeaService, public dialog: MatDialog) {
const batchMap = this.offset.pipe(
throttleTime(500),
mergeMap(n => this.getIdeasFromServer(n)),
scan((acc, batch) => {
return { ...acc, ...batch };
}, {})
);
this.infinite = batchMap.pipe(map(v => Object.values(v)));
console.log(this.infinite);
}
nextBatch(e: any, offset: any) {
if (this.isEndOfTheList) {
return;
}
const end = this.viewport.getRenderedRange().end;
const total = this.viewport.getDataLength();
if (end === total) {
this.offset.next(offset);
}
}
trackByIndex(i: any) {
return i;
}
ngOnInit() {
this.ideaService.getTotalIdeaCount().subscribe(data => {
this.length = data.data.ideas_aggregate.aggregate.count;
});
}
getIdeasFromServer(pageIndex: any) {
console.log(this.ideaList, this.pageIndex);
return this.ideaService.getNIdeas(batchSize, pageIndex).pipe(
map((data: any) => {
this.pageIndex += 10;
data.data.ideas.forEach((idea: any) => {
this.ideaList.push(idea);
});
this.ideaList = data.data.ideas;
return data.data.ideas;
})
);
}
}
Here's the HTML code snippet for feed.component.html:
<div *ngIf="(infinite | async) as ideaList" class="ideafeed-background">
<cdk-virtual-scroll-viewport itemSize="100" scrolledIndexChange)="nextBatch($event, pageIndex)">
<mat-card
class="idea-card"
*cdkVirtualFor="let item of ideaList; let i = index; trackBy: trackByIdx">
{{ item.name }}
</mat-card>
</cdk-virtual-scroll-viewport>
</div>
I am unsure of what mistake I might be making. I have followed a tutorial which can be found here.