I'm working with a PrimeNG p-table that uses lazy loading and virtual scrolling in my Angular application. However, I've encountered an issue where the onLazyLoad method is being triggered multiple times (two or three times) on the initial load instead of just once. I don't get the totalRecords from backend,can that be causing this issue Here’s a simplified version of my code: <p-table #dt class="lme" [ngClass]="{ 'no-records': myRecords?.length === 0, 'few-records': myRecords?.length > 0 && myRecords?.length <= 2, 'small-dataset': myRecords?.length > 2 && myRecords?.length < 6 }" [scrollable]="true" scrollHeight="calc(100vh - 180px)" [style]="{ width: '100%' }" [columns]="selectedColumns" [reorderableColumns]="true" [resizableColumns]="true" [virtualScroll]="true" [virtualRowHeight]="virtualRowHeight" [virtualScrollItemSize]="virtualRowHeight" [virtualScrollDelay]="200" [rows]="pageSize" [value]="myRecords" [lazy]="true" (onLazyLoad)="onLazyLoad($event)" [responsive]="true" [(selection)]="selectedMyRecords" (onRowSelect)="rowClick($event.originalEvent, $event.data)" dataKey="id" > <ng-template pTemplate="caption"> <div style="text-align: left"> <p-multiSelect [options]="columns" [(ngModel)]="selectedColumns" optionLabel="columnValue" selectedItemsLabel="{0} columns selected" [style]="{ minWidth: '200px' }" defaultLabel="Choose Columns" > </p-multiSelect> <button id="saveDefaultView" class="ml-2 btn btn-primary save-default-view" (click)="saveDefaultView()" > Save Default View </button> </div> </ng-template> <ng-template pTemplate="header" let-columns> <tr> <th *ngIf=" user.stakeHolder === 'METAL_OWNER' || user.stakeHolder === 'LME_MEMBER' " > <p-tableHeaderCheckbox wcag-label (click)="onHeaderSelect($event)" ></p-tableHeaderCheckbox> </th> <th [ngClass]="{ 'non-checkbox-col': !col.checkboxSelect, 'checkbox-col': col.checkboxSelect }" *ngFor="let col of columns" pReorderableColumn pResizableColumn > <span [attr.cy-test]="col.columnValue" id="{{ col.columnValue }}" >{{ col.columnValue }}</span > <!-- <p-sortIcon id="{{ col.field }}" [field]="col.sortableColumnName" *ngIf="col.sortableColumnName !== 'NONE'" ></p-sortIcon> --> </th> </tr> </ng-template> <ng-template pTemplate="body" let-rowData let-columns="columns"> <tr [pSelectableRow]="rowData"> <td *ngIf=" user.stakeHolder === 'METAL_OWNER' || user.stakeHolder === 'LME_MEMBER' " > <p-tableCheckbox [value]="rowData" wcag-label></p-tableCheckbox> </td> <td *ngFor="let col of columns" class="ui-resizable-column"> <ng-container *ngIf=" col.columnKey !== 'CHEMICAL_COMPOSITION'; else CHEMICAL_COMPOSITION " > <ng-container *ngIf=" col.columnKey !== 'ORIGINAL_COS_DOCUMENTS'; else DOWNLOAD_COA " > <!-- <ng-container *ngIf="!(col.columnKey === 'COA_DATE' || col.columnKey === 'DATE_OF_PRODUCTION'); else COA_DATE"> --> <ng-container *ngIf=" col.columnKey !== 'VALUE_ADD_THEMES_DISCLOSURE'; else VALUE_ADD_THEMES_DISCLOSURE " > <ng-container *ngIf="col.columnKey !== 'CBAM'; else CBAM"> <ng-container *ngIf=" col.columnKey !== 'SCR_COMPLIANT'; else SCR_COMPLIANt " > {{ rowData[col.columnHeaderInSearchResult] }} </ng-container> </ng-container> </ng-container> <!-- </ng-container> --> </ng-container> </ng-container> <ng-template #CHEMICAL_COMPOSITION> <span *ngIf="rowData.recordType === 'ENHANCED'; else BASIC" class="link" role="button" tabindex="0" (click)="viewComposition($event, rowData)" (keyup)="viewComposition($event, rowData)" >View Chemical Composition</span > </ng-template> <ng-template #BASIC> NA </ng-template> <ng-template #DOWNLOAD_COA> <span role="button" tabindex="0" (click)="exportCoa($event, rowData)" (keyup)="exportCoa($event, rowData)" class="link" >Export Original COA</span > </ng-template> <ng-template #COA_DATE> {{ col.columnHeaderInSearchResult }} {{ rowData[col.columnHeaderInSearchResult] | date : "dd MMMM, yyyy" }} </ng-template> <ng-template #VALUE_ADD_THEMES_DISCLOSURE> <span class="link" role="button" tabindex="0" *ngIf="rowData.themesAttached" (click)="viewValueAddThemes($event, rowData)" (keyup)="viewValueAddThemes($event, rowData)" >Details</span > <span *ngIf="!rowData.themesAttached">NA</span> </ng-template> <ng-template #CBAM> <span *ngIf="rowData.recordType === 'ENHANCED'; else BASIC" class="link" role="button" tabindex="0" (click)="exportCbam($event, rowData)" (keyup)="exportCbam($event, rowData)" >Export CBAM report</span > </ng-template> <ng-template #SCR_COMPLIANt> {{ rowData[col.columnHeaderInSearchResult] }} <span *ngIf="rowData.remarks" class="link" tabindex="0" role="button" ><i class="pi pi-info-circle ml-2" tabindex="0" role="button" aria-label="Info" (click)="viewScrComplianceInfo($event, rowData)" (keyup)="viewScrComplianceInfo($event, rowData)" ></i ></span> </ng-template> </td> </tr> </ng-template> <ng-template pTemplate="emptymessage" let-columns> <tr> <td [attr.colspan]="selectedColumns?.length">No records found</td> </tr> </ng-template> <ng-template pTemplate="loadingbody"> <tr> <td [attr.colspan]="selectedColumns?.length + 1"> <div class="loading-text">Loading records...</div> </td> </tr> </ng-template> </p-table> onLazyLoad(event) { if (!this.isManualTriggerActive) { this.lastNaturalLazyLoadTime = Date.now(); if (this.scrollMonitorTimeout) { clearTimeout(this.scrollMonitorTimeout); this.scrollMonitorTimeout = null; } } if (this.lazyLoadTimeout) { clearTimeout(this.lazyLoadTimeout); } if (this.loading || this.isProcessingLoad) { const timeSinceLastCall = Date.now() - this.lastApiCallTime; if (timeSinceLastCall > 30000) { this.loading = false; this.isProcessingLoad = false; } else { return; } } if (!this.hasMoreData) { return; } if (event.sortField) { let sortKey = event.sortField; let sortCol; let colIndex = this.defaultView.findIndex( (col) => col.columnHeaderInSearchResult === sortKey ); colIndex !== -1 && (sortCol = this.defaultView[colIndex]["columnKey"]); this.filters["columnSortStatus"] = {}; this.filters["columnSortStatus"][sortCol] = event.sortOrder === 1 ? "ASC" : "DESC"; // Reset for new sort console.log("Sort changed, resetting data"); this.myRecords = []; this.currentPage = 0; this.hasMoreData = true; this.isProcessingLoad = true; this.loading = true; this.totalRecords = 0; this.filters["keyword"] = this.filters["freeText"]; this.getSearchDataVirtual(this.filters); return; } // For initial load (when no data loaded yet) if (this.myRecords.length === 0) { console.log("Initial load"); this.isProcessingLoad = true; this.loading = true; this.filters["keyword"] = this.filters["freeText"]; this.getSearchDataVirtual(this.filters); return; } // For virtual scroll, check if we need to load more data // Adaptive loading based on dataset size const visibleEndIndex = event.first + event.rows; // Use smaller buffer for smaller datasets to ensure loading triggers const adaptiveBufferPercent = this.myRecords.length < 200 ? 0.2 : 0.5; const bufferSize = Math.max( 10, Math.floor(this.pageSize * adaptiveBufferPercent) ); const loadThreshold = this.myRecords.length - bufferSize; if (visibleEndIndex < loadThreshold) { console.log("Skipping load - not approaching end of loaded data"); return; } const currentTime = Date.now(); const timeSinceLastCall = currentTime - this.lastApiCallTime; const timeNeeded = Math.max( 0, this.minTimeBetweenCalls - timeSinceLastCall ); if (this.lazyLoadTimeout) { clearTimeout(this.lazyLoadTimeout); } const totalDelay = Math.max(150, timeNeeded); this.lazyLoadTimeout = setTimeout(() => { if (this.hasMoreData && !this.loading && !this.isProcessingLoad) { this.isProcessingLoad = true; this.loading = true; this.filters["keyword"] = this.filters["freeText"]; this.getSearchDataVirtual(this.filters); } }, totalDelay); } Observations: The onLazyLoad method is called multiple times during the initial load. I have implemented guards (isProcessingLoad and loading), but they don’t seem to prevent the multiple calls. I am using both lazy loading and virtual scrolling features of PrimeNG. Questions: What could be causing the onLazyLoad method to trigger multiple times on initial load? How can I effectively prevent this behavior while still allowing legitimate lazy loading to occur when the user scrolls? Additional context: I am using PrimeNG version 14 with Angular version 14 The table is configured to support virtual scrolling, and the data is loaded from an API. Continue reading...