Is it possible to maintain the scroll position of a PrimeNG table when updating the data?

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!

Answer №1

After some investigation, I have identified the root cause of the problem - PrimeNG's default table behavior triggers the function resetScrollTop() each time a new filter is applied.

My table has multiple filters (text, boolean, etc.) which are reapplied every 500 milliseconds when the table data is updated, leading to the constant resetting of the scroll position (as shown in the PrimeNG source code here).

To address this issue, I took a workaround by overriding the function specifically for this table:

location-component.html

<p-table #locationsTable ...> ... </p-table>

location-component.ts

ngAfterViewInit() {
    if (this.locationsTable != null) {
        this.locationsTable.resetScrollTop = function() { }
    }
}

While this solution seems to be effective for now, I am open to better alternatives. Nonetheless, I hope this explanation can assist others facing a similar issue in the future.

Answer №2

ngAfterViewInit(): void {
  this.adjustSizeToFixBug();
}

/**
 * Disable auto sizing of the scroller to resolve the known issue:
 * https://github.com/primefaces/primeng/issues/12740
 */
adjustSizeToFixBug(): void {
  if (this.isVirtual) {
    this.pTable.scroller.autoSize = false;
  }
}

//Source: https://github.com/primefaces/primeng/issues/12740

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to include all visible content, even when scrolling, within the full page height?

My webpage contains content that exceeds the height of the window. I am looking for a way to retrieve the full height of my page using either jQuery or pure Javascript. Can anyone provide a solution? I have considered the following approach: $('body ...

Incoming information obtained via Websocket

Currently, I am working with Angular and attempting to retrieve data from the server using websockets. Despite successfully receiving the data from the server, I am faced with a challenge where instead of waiting for the server to send the data, it retur ...

Derive the property type based on the type of another property in TypeScript

interface customFeatureType<Properties=any, State=any> { defaultState: State; properties: Properties; analyzeState: (properties: Properties, state: State) => any; } const customFeatureComponent: customFeatureType = { defaultState: { lastN ...

What is the method for accessing the value of variable "a" in the following code?

request(target, function (err, resp, body) { $ = cheerio.load(body); links = $('a'); $(links).each(function (i, link) { if ($(link).text().match('worker')) { var a = $(link).attr('href').toStri ...

The Jquery click event refuses to trigger

Below is the JavaScript code that is not functioning: $('#change-priority-modal').find('.btn-primary').unbind().on("click", function () { //my_func_body } Interestingly, the code works perfectly fine in the developer console. I would ...

Encountering issues during postinstall when trying to npm install the classlist.js package using

I am facing an issue on Windows 10 with the latest update. My npm version is 5.4.1 and despite trying multiple solutions from various sources like StackOverflow and GitHub, I am unable to resolve it. Some of the things I have tried are: Using command pro ...

Swapping out the title attribute within imgbox

After updating to the latest version of imgbox, I encountered a similar issue as I did with LightBox2 involving the 'title' attribute. It creates a caption, but the HTML tends to show a hovertext over the image link. Here is an example: <a ...

Rearrange the order of items in the fancybox gallery

Typically, fancybox displays items in the gallery based on the order they are added in the HTML code. Is there a way to customize the order of items when they are opened in the popup, while keeping the original order when displayed on the page? The solut ...

Button-operated lightbox image slider

I am currently developing a website on WAMP where users can store images and view them in a lightbox effect. I have successfully implemented the lightbox effect, but I am facing challenges with moving between images within the lightbox. <html> <? ...

Transforming a mongodb operation into an asynchronous function using await and async syntax

After calling the function to retrieve data from MongoDB, an undefined error occurs. It is suspected that converting the function to an async/await function may resolve this issue. However, there is uncertainty on how to make this conversion without disrup ...

What steps can be taken to resolve the issue "AG Grid: Grid creation unsuccessful"?

For my project, I decided to use the modular import approach for AG-Grid. This means importing only the necessary modules instead of the entire package: "@ag-grid-community/core": "31.3.2", "@ag-grid-community/react": ...

Adjusting the value of 'let' based on the outcome

Can you assist me with this issue? I am attempting to assign a value to a let variable based on the following if conditional block. class Main extends React.Component{ render(){ let content = ''; firebase.auth().onAuthStateChanged(func ...

Wrap each object in a container and then insert its key and values into that container using jQuery

How can I wrap each item and then insert the object's indexes and values into each created wrapper? I've attempted to accomplish this using the following code: $.ajax({ url: "some url", type: "GET", success: function(data) { var data ...

What is the best way to have an icon appear when a child component div is clicked, without it displaying on other similar divs?

Within my child component div, I have configured it to display information from an object located in the parent component. Initially, when the web app loads, it correctly shows three divs with names and messages retrieved from the created object. However, ...

How many files are being monitored by Grunt?

Recently, I received a new project using Angular + Node from a client and successfully set it up on my local machine. However, one major issue arose when running the grunt command - my CPU spiked to 100% causing my system to hang. Strangely, the same proje ...

Customize a div's background color with an Angular directive

Imagine having a div element: <div id="wrapper"> some text </div> How can you create an angular directive that changes the background based on user input? For instance, you might have tried: <div id="wrapper" color temperature="51"> ...

Tips for establishing conditional guidelines and mandatory fields within Vue?

Struggling to implement conditional required fields on a form. The approach I've taken is by setting a data field notRequired: false, and then using it in the fields like this: :required="!notRequired". This allows me to make certain fields not requir ...

Looping through an array in Vue using v-for and checking for a specific key-value pair

As I dive into my first Vue app, I've encountered a minor setback. Here's my query: How can I iterate through a list of dictionaries in Vue, specifically looping through one dictionary only if it contains a certain value for a given key? Provi ...

Stop observing changes from the store in NgRx effects for easy management of

I am implementing a straightforward effect: @Effect() simpleEffect = this.actions.pipe( ofType<ActionA>('Action A'), withLatestFrom(this.store.select(selectSomething)), map(([action, something]) => console.log('CALLED TWICE&ap ...

Struggling to properly render JSON data

Having trouble accessing specific elements in a script that merges local JSON files using AJAX? The script works fine in Chrome Console, but you can't reach certain elements like 'object.country'. Any suggestions on how to tackle this issue? ...