I'm currently working on a project with PrimeNG 12 tables using Angular 12, but I'm having a bit of trouble with Angular's change detection and table updates, specifically with trying to prevent a PrimeNG p-table
from scrolling back to the top on data refresh.
For some background, I have a service (let's call it LocationService
) that is responsible for holding a list of locations and updating them every, say, 500 milliseconds. Something like the following:
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);
});
}
Now, I've got a component that subscribes to the location updates and displays them as (Lat, Lng)
in a p-table
, but also has the requirement that the longitude is between -45.0 and 45.0 degrees (other components may not have this requirement). Scroll order doesn't matter, although most of the time it's in the same order since the rows aren't changing all that much.
I am currently subscribing to it like so, using virtual scrolling as there may be hundreds of potential 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 issue here is that when I run the code, the table snaps to the top every time the data is refreshed. I presume this is due to the fact that the array reference changes. However, if I move away from the async
pipe and use a local array, simply modifying it on updates like so:
.subscribe(locations => {
this._locations.splice(0, this._locations.length, ...locations.filter(location => location.lng >= -45.0 && location.lng <= 45.0));
this._changeDetectorRef.detectChanges();
}
Angular doesn't detect the change, even with he detectChanges()
call (it seems to do nothing). And using something like this._locations = this._locations.slice()
to notify it another way just brings me back to the problem above.
My question to y'all is this: does anyone have any tips on how I might solve this problem? My goal is to refresh the data every X milliseconds but to also allow the user to scroll as it updates.
Any help would be greatly appreciated!