PrimeNG TurboTable - strange virtual scroll behaviour - keeps doubling event.rows
Asked Answered
S

2

6

I've implemented a custom Grid component that uses PrimeNG TurboTable and NgRx and I've added the virtual scroll feature to it using the built-in TurboTable virtual scroll. It is also lazy loading, with onLazyLoad handler. However, I get this strange behaviour when onLazyLoad is called as I am scrolling through the grid results.

onLazyLoad called with event.first=0 and event.rows=40
onLazyLoad called with event.first=0 and event.rows=80
onLazyLoad called with event.first=0 and event.rows=160
onLazyLoad called with event.first=0 and event.rows=320
onLazyLoad called with event.first=0 and event.rows=640
onLazyLoad called with event.first=0 and event.rows=1280

So there are 2 problems:

  1. event.first always stays 0
  2. event.rows keeps doubling to very high values

I thought that it's a bug in PrimeNG but when I've tried to reproduce it with a simple StackBlitz, there it is behaving correctly. https://stackblitz.com/edit/github-primeng-virtualscroll-issue

I see this on StackBlitz:

loadDataOnScroll is called with event.first=0 and event.rows=40
loadDataOnScroll is called with event.first=20 and event.rows=40
loadDataOnScroll is called with event.first=40 and event.rows=40
loadDataOnScroll is called with event.first=80 and event.rows=40
loadDataOnScroll is called with event.first=120 and event.rows=40

So event.rows stays constant as it is supposed to and event.first increases normally.

Looks like something is triggering this from the way I use the TurboTable, but I don't know what. What could be the issue? Here is the relevant code from my grid component:

The lazy load handler:

onLazyLoad(event: LazyLoadEvent) {
    // TODO - In this StackBlitz it behaves correctly
    // https://stackblitz.com/edit/github-primeng-virtualscroll-issue
    console.log(
        `onLazyLoad called with event.first=${event.first} and event.rows=${
            event.rows
        }`
    );

    // TODO - This state should be handled by NgRx as well.
    this.loading = true;
    this.filters = {};

    const hasPagination = this.settings.features.Pagination;

    // TODO - Tweak the behavior of virtual scroll skipCount/maxResultCount
    // based on testing results.
    // The default behavior is a bit strange, skipCount is always 0
    // and maxResultCount keeps getting doubled.
    let pageSize = event.rows ? event.rows : GridConfig.PageSize;
    this.filters.maxResultCount = pageSize;
    this.filters.skipCount = event.first ? event.first : 0;
    this.filters.ignorePagination = !hasPagination;

    if (event.sortOrder && event.sortField) {
        const sortingDirection = event.sortOrder > 0 ? 'ASC' : 'DESC';
        this.filters.sorting = event.sortField + ' ' + sortingDirection;
    }

    if (event.globalFilter) {
        this.filters.filter = event.globalFilter;
    }

    if (event.filters) {
        this.filters.columnFilters = this.buildColumnFilters(event.filters);
    }

    this.filters.parentFilters = this.parentFilters; // this works only with client-side caching & filtering

    if (this.settings.features.ClientSideCaching) {
        // Load only once
        this.gridLoaded().subscribe(loaded => {
            if (loaded) {
                this.store.dispatch(
                    new this.settings.stateActions.FilterClientSide(
                        this.filters
                    )
                );
            }
        });
    } else {
        this.store.dispatch(
            new this.settings.stateActions.Read(this.filters)
        );
    }
}

The parameters I pass in the template:

<p-table
        #dataTable
        [value]="(data$ | async)?.items"
        [loading]="loading"
        [lazy]="true"
        [virtualScroll]="!settings.features.Pagination"
        [virtualRowHeight]="35"
        [scrollable]="!settings.features.Pagination"
        (onLazyLoad)="onLazyLoad($event)"
        [autoLayout]="true"
        [columns]="selectedColumns"
        [paginator]="settings.features.Pagination"
        scrollHeight="400px"
        [resizableColumns]="true"
        [rowsPerPageOptions]="[10, 20, 30]"
        [totalRecords]="(data$ | async)?.totalCount"
        [rows]="(this.settings.states.Search | async)?.maxResultCount"
        [stateStorage]="this.settings.persistence?.stateStorage"
        [stateKey]="this.settings.persistence?.stateKey"
        [(selection)]="selectedRows"
        [selectionMode]="this.settings.rowSelection?.selectionMode || null"
        (onRowSelect)="onRowSelect($event)"
        [dataKey]="this.settings.rowSelection?.dataKey || null"
    >

Any help or ideas would be much appreciated! Thanks!

Savell answered 10/4, 2019 at 8:35 Comment(0)
S
2

The problem happened because event.rows is persisted in the NgRx store and I was reusing it when building the filters and every time onLazyLoad gets called it would get doubled.

Here I initialize rows with the NgRx store value:

[rows]="(this.settings.states.Search | async)?.maxResultCount"

Then inside onLazyLoad I was using event.rows:

let pageSize = event.rows ? event.rows : GridConfig.PageSize;
this.filters.maxResultCount = pageSize;

PrimeNG doubles event.rows in their code for virtual scroll and it gets persisted into the NgRx store when I call FilterClientSide action. Then the template gets that new value and on a subsequent onLazyLoad it gets doubled again.

The solution that worked for me was to just set a constant for this:

if (this.settings.features.VirtualScroll) {
    pageSize = GridConfig.PageSize * 2;
} else {
    pageSize = event.rows ? event.rows : 10;
}
Savell answered 23/4, 2019 at 14:19 Comment(1)
I am not using NgRx store, but I still have the same problem. The onLazyLoad() method gets called twice at initial loading of the table. Could you please help me?Into
B
2

This could be because in the https://stackblitz.com/edit/github-primeng-virtualscroll-issue you have a hard-coded total record count parameter to <p-table>. Where as in your actual implementation you may be setting to something smaller than your dataset size or 0 (Looks like you have an observable there. Check on what does it evaluate to. Try setting some large number greater than your dataset to see if this theory is correct).

Check on what does [totalRecords]="(data$ | async)?.totalCount" parameter evaluates to you in your implementation for <p-table>. Hopefully this will fix it.

Beore answered 17/4, 2019 at 22:51 Comment(1)
Thanks for the response. Unfortunately, this is not the case. I've tried hardcoding 1000 to totalRecords (which is greater than my dataset) and the behaviour is the same. I think somewhere the pagination settings are persisted and keep getting doubled instead of just getting recalculated from the base pagesizeKerri
S
2

The problem happened because event.rows is persisted in the NgRx store and I was reusing it when building the filters and every time onLazyLoad gets called it would get doubled.

Here I initialize rows with the NgRx store value:

[rows]="(this.settings.states.Search | async)?.maxResultCount"

Then inside onLazyLoad I was using event.rows:

let pageSize = event.rows ? event.rows : GridConfig.PageSize;
this.filters.maxResultCount = pageSize;

PrimeNG doubles event.rows in their code for virtual scroll and it gets persisted into the NgRx store when I call FilterClientSide action. Then the template gets that new value and on a subsequent onLazyLoad it gets doubled again.

The solution that worked for me was to just set a constant for this:

if (this.settings.features.VirtualScroll) {
    pageSize = GridConfig.PageSize * 2;
} else {
    pageSize = event.rows ? event.rows : 10;
}
Savell answered 23/4, 2019 at 14:19 Comment(1)
I am not using NgRx store, but I still have the same problem. The onLazyLoad() method gets called twice at initial loading of the table. Could you please help me?Into

© 2022 - 2024 — McMap. All rights reserved.