I've encountered a challenge while working on a project involving PrimeNG 12 tables with Angular 12. The issue lies in Angular's change detection mechanism and updating table data, specifically in preventing a PrimeNG p-table
from scrolling back to the top during data refresh.
To provide context, I have a service (let's call it LocationService
) that manages a list of locations and updates them approximately every 500 milliseconds. Here's a simplified example of the setup:
location.ts
interface Location {
id: string;
lat: number;
lng: number
}
location.service.ts
// RxJS Behaviour Subjects
_locationUpdates$: BehaviorSubject<Location[]> = new BehaviorSubject<Location[]>([]);
// RxJS Observables
locationUpdates$: Observable<Location[]> = _locationUpdates.asObservable();
// Service Function
startUpdates() {
// Called within a service
timer(0, 500).subscribe(() => {
this._locations.forEach(location => {
location.lat = // ... some method to calculate new latitude
location.lng = // ... some method to calculate new longitude
});
this._locationUpdates$.next(this._locations);
});
}
I have a component that subscribes to the location updates and displays them as (Lat, Lng)
in a p-table
. One of the requirements is that the longitude falls within the range of -45.0 to 45.0 degrees. The order of the rows doesn't matter much, although they mostly stay in the same order as the data isn't changing dramatically.
I'm currently subscribing to the updates with virtual scrolling enabled, as there could be a large number of rows:
location-component.html
<ng-container *ngIf="locations$ | async as locations">
<p-table
dataKey="id"
scrollHeight="flex"
[rows]="100"
[scrollable]="true"
[value]="locations"
[virtualScroll]="true"
[virtualRowHeight]="34"
>
<ng-template pTemplate="header">
<tr>
<th>Latitude</th>
<th>Longitude</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-location>
<tr>
<td>{{ location.lat }}</td>
<td>{{ location.lng }}</td>
</tr>
</ng-template>
</p-table>
</ng-container>
location-component.ts
// RxJS observables
locations$: Observable<Location[]>;
// Constructor
constructor(private _locationService: LocationService) {
locations$ = _locationService.locationUpdates$
.pipe(
map(locations => locations.filter(location => location.lng >= -45.0 && location.lng <= 45.0))
)
}
The problem arises when the table snaps back to the top every time the data is refreshed. This behavior is likely caused by the change in the array reference. Even if I avoid using the async
pipe by manipulating a local array directly on updates, Angular fails to detect the change:
.subscribe(locations => {
this._locations.splice(0, this._locations.length, ...locations.filter(location => location.lng >= -45.0 && location.lng <= 45.0));
this._changeDetectorRef.detectChanges();
}
Even with the detectChanges()
method called, the change goes unnoticed. Attempting to workaround using
this._locations = this._locations.slice()
only leads back to the initial issue.
My query to the community is this: Does anyone have suggestions on how I can address this issue? My objective is to refresh the data at regular intervals while allowing the user to scroll through the updates seamlessly.
Any assistance on this matter would be highly appreciated!