primeng icon indicating copy to clipboard operation
primeng copied to clipboard

Table: Scroll reset on value change

Open 123FLO321 opened this issue 2 years ago • 5 comments

Describe the bug

When a p-table has p-columnFilter column filters (or the filters array is set) and its value is updated the view is always scrolled to the top, even when the filters did not change. Without a filter set the scroll is not reset. This happens with and without virtual scroll.

at Table.resetScrollTop (primeng-table.mjs:1218:18)
at Table._filter (primeng-table.mjs:1084:18)
at Table.sortSingle (primeng-table.mjs:544:26)
at Table.ngOnChanges (primeng-table.mjs:269:26)
at Table.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:1573:14)
at callHook (core.mjs:2497:18)
at callHooks (core.mjs:2456:17)
at executeCheckHooks (core.mjs:2388:5)
at refreshView (core.mjs:10375:21)
at refreshComponent (core.mjs:11427:13)

Environment

See StackBlitz.

Reproducer

https://stackblitz.com/edit/github-g1xm1w?file=src/app/app.component.html

Angular version

15.2.0

PrimeNG version

15.2.0

Build / Runtime

Angular CLI App

Language

TypeScript

Node version (for AoT issues node --version)

18.13.0

Browser(s)

Chrome 111

Steps to reproduce the behavior

  1. Create a scrollable p-table
  2. Define a p-columnFilter on the table
  3. Scroll in the table
  4. Update the value property of the table

Expected behavior

The scroll should not be reset unless the filter changed.

123FLO321 avatar Mar 12 '23 12:03 123FLO321

I have a similar issue, but it is not depending on column filters, simply adding items to the list will cause the scroll to reset to the top. This makes the table very frustrating to use unless the data source is static.

I identified the cause as the calculateAutoSize method in Scroller component changing contain css property, causing the scroll position to reset.

this.elementViewChild.nativeElement.style.contain = 'none'

Minimal reproduction, based on virtual scroll table example

https://stackblitz.com/edit/nxgtoj?file=src%2Fapp%2Fdemo%2Ftable-virtual-scroll-demo.html

omnight avatar Sep 19 '23 08:09 omnight

I'm facing a similar issue since updating to Angular 16 and PrimeNG 16. Previously, I used this method to prevent scrolling to the top when adding elements to my table:

ngAfterViewInit() {
    // To prevent scrolling to the top
    if (this.dt != null) {
        this.dt.resetScrollTop = function () {}
    }
}

This method stop the scroll top action, but after the upgrade, it seems to no longer work. Has anyone else faced this issue or found a workaround?

I've noticed that this issue occurs specifically with tables that use virtual scrolling. When I use a table with non-virtual scrolling, the function I mentioned earlier works as expected. Is there a similar method for virtual scroll tables in the current version of PrimeNG?

Ekhynox avatar Jan 05 '24 16:01 Ekhynox

I'm facing a similar issue since updating to Angular 16 and PrimeNG 16. Previously, I used this method to prevent scrolling to the top when adding elements to my table:

ngAfterViewInit() {
    // To prevent scrolling to the top
    if (this.dt != null) {
        this.dt.resetScrollTop = function () {}
    }
}

This method stop the scroll top action, but after the upgrade, it seems to no longer work. Has anyone else faced this issue or found a workaround?

I've noticed that this issue occurs specifically with tables that use virtual scrolling. When I use a table with non-virtual scrolling, the function I mentioned earlier works as expected. Is there a similar method for virtual scroll tables in the current version of PrimeNG?

To fix the issue I had to set the autoSize Input property of the scroller to false:

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

/**
 * Set autoSize of the scroller to false in order to fix the following issue:
 * https://github.com/primefaces/primeng/issues/12740
 */
fixAutoSizeIssue(): void {
  if (this.isVirtual) {
    this.pTable.scroller.autoSize = false;
  }
}

Where pTable is the PrimeNG table. I don't know why they've hardcoded the autoSize to true without giving any chance to control it by passing an Input to the table component. Hope this helps!

simeon-nikolov avatar Jan 13 '24 17:01 simeon-nikolov

Thank you very much for your solution! I followed your advice by setting the "autoSize" property to false, and that solved my problem. Like you, I'm wondering why this option is hard-coded to true with no way to control it via an Input. I'm sure others will benefit from this solution too. Thanks again !

Ekhynox avatar Feb 05 '24 09:02 Ekhynox

In case you do need to auto-size the table, here is the workaround hack I've been using. The only thing changed are two lines added. This is just temporary until a proper fix is made and may very well break in later updates.

import { Scroller } from "primeng/scroller"
import { DomHandler } from "primeng/dom"
import { ElementRef } from "@angular/core"
...

Scroller.prototype.calculateAutoSize = function() {
    if (this._autoSize && !this.d_loading) {
        Promise.resolve().then(() => {
            if (this.contentEl) {
                this.contentEl.style.minHeight = this.contentEl.style.minWidth = 'auto';
                this.contentEl.style.position = 'relative';

                let originalScrollPosition = this.elementViewChild.nativeElement.scrollTop;  // ADDED

                (<ElementRef>this.elementViewChild).nativeElement.style.contain = 'none';

                const [contentWidth, contentHeight] = [DomHandler.getWidth(this.contentEl), DomHandler.getHeight(this.contentEl)];
                contentWidth !== this.defaultContentWidth && ((<ElementRef>this.elementViewChild).nativeElement.style.width = '');
                contentHeight !== this.defaultContentHeight && ((<ElementRef>this.elementViewChild).nativeElement.style.height = '');

                const [width, height] = [DomHandler.getWidth((<ElementRef>this.elementViewChild).nativeElement), DomHandler.getHeight((<ElementRef>this.elementViewChild).nativeElement)];
                (this.both || this.horizontal) && ((<ElementRef>this.elementViewChild).nativeElement.style.width = width < <number>this.defaultWidth ? width + 'px' : this._scrollWidth || this.defaultWidth + 'px');
                (this.both || this.vertical) && ((<ElementRef>this.elementViewChild).nativeElement.style.height = height < <number>this.defaultHeight ? height + 'px' : this._scrollHeight || this.defaultHeight + 'px');

                this.contentEl.style.minHeight = this.contentEl.style.minWidth = '';
                this.contentEl.style.position = '';
                (<ElementRef>this.elementViewChild).nativeElement.style.contain = '';

                this.elementViewChild.nativeElement.scrollTop = originalScrollPosition;      // ADDED
            }
        });
    }
}

omnight avatar Feb 12 '24 09:02 omnight