Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mat-table performance issues - once the table has loaded, it freezes the page

I have a mat-table that has over 150 rows, which doesn't seem like much but it completely freezes the page (especially if I have the developer console open).

To set the table data I use an input getter and setter (the reason being that it then allows me to make changes in the parent component and the child component will listen.)

@Input()
get data() {
    return this.dataSource;
}
set data(tableData: any) {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
    this.dataSource.data = tableData;
    // moment dates cant be sorted by mat-table: this is their 
    // recommendation to convert it to timestamp to be sorted 
    // (still displays as moment date tho :D)
    this.dataSource.sortingDataAccessor = (item, property) => {
        switch (property) {
            case 'momentDueDate': return new Date(item.momentDueDate);
            default: return item[property];
        }
    };
}

The data itself loads relatively quick, however, as soon as I click anything on the page, the page freezes. This even includes trying to change page using mat-pagination on the table.

In my parent component, my data is created by an observable using combineLAtest like so:

this.combinedPaymentsAndfilters$ = combineLatest(
    [this.combinedPaymentDetails$,
        this.dateFilter$,
        this.dateToFrom.asObservable(),
        this.typeFilter$,
        this.sortFilter$, 
        this.isAdmin$]).pipe(
            tap(([payments, dateFilter, dateToFrom, type, sort, isAdmin]) => {
        this._router.navigate(['.'], {
            queryParams: {
                date: dateFilter,
                dateFrom: dateToFrom.date_from,
                dateTo: dateToFrom.date_to,
                type: type, 
                order: sort
            },
            relativeTo: this._route,
            replaceUrl: true
        });
    }),
    map(([payments, dateFilter, dateToFrom, type, sort, isAdmin]) => {
        let combined = this.sortPayments(sort, payments);
        if (dateFilter !== 'all') {
            combined = this.filterByDate(payments, dateFilter, dateToFrom);
        }
        if (type !== 'all') {
            combined = combined.filter(
                (payment: any) => payment.paymentType === type);
        }
        this.paymentTitle = this.getViewValue(this.paymentTypeFilter, type);
        this.dateTitle = this.getViewValue(this.dateFilterType, dateFilter);
        return {
            combined,
            isAdmin
        };
    })
);

I also get the following chrome violations whilst in this component:

enter image description here

Other things I have tried:

So I have looked at a few similar things online, and people have recommended loading the data after the pagination etc (which you can see above I have). Others have also recommended using AfterViewInit, I have also tried this, but it still makes no difference. As a way of testing whether there was something else erroring in my code, I have also limited my firestore query to only return 5 items. Once I have done this, it works absolutely fine, which tells me the issue is definitely with the amount of data I am trying to display.

Any recommendations on improving this performance as currently, its unusable for production.

EDIT - The issue only really seems to happen when the chrome console is open

like image 930
Jm3s Avatar asked Sep 01 '25 22:09

Jm3s


2 Answers

Complementing the answer of satanTime, if the combineLatest persists to be spammy even if distinctUntilChanged, you can use the operator debounceTime to force some milliseconds of silence and then get the last result.

Another angular list optimization is to use trackBy.

like image 147
Yasser Nascimento Avatar answered Sep 03 '25 16:09

Yasser Nascimento


In this case I would say that too many emits are happening in combineLatest. Perhaps with the same data in case if its filter wasn't changed.

every emit can cause changes of pointers even data is the same and it can cause a new render of the table.

I would add distinctUntilChanged as the first pipe operator to ensure that we really need to emit new value.

It's the implementation with JSON.encode.

.pipe(
   distinctUntilChanged((prev, curr) => JSON.encode(prev) !== JSON.encode(curr))
   ...
)

Then regardless of emits with the same values your pipe won't react until there's a real change.

like image 26
satanTime Avatar answered Sep 03 '25 18:09

satanTime