4

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

Jm3s
  • 556
  • 2
  • 12
  • 24
  • Most likely there's too much going on in Combined payments function. Try to reduce work into smaller async parts yeilding small but continuous results. You will be able to see areas to improve. – JWP May 12 '20 at 10:39
  • @JohnPeters Could you expand on that please? `Try to reduce work into smaller async parts yeilding small but continuous results` – Jm3s May 12 '20 at 10:43

3 Answers3

3

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.

Yasser Nascimento
  • 1,302
  • 7
  • 13
  • @satanTime So, I have added a debounce to the end of the combineLatest, but the problem is once the data has loaded, when I try to change page on the mat-table pagination, there is a real lag - almost seems as if nothing is happening – Jm3s May 12 '20 at 12:31
  • it should be the first operator in the pipe of combineLatest, not the latest argument in it. – satanTime May 12 '20 at 12:33
  • @satanTime Added to the start but still getting the same lag when changing pagination – Jm3s May 12 '20 at 12:38
  • I see, sad sad, what about distinctUntilChanged? does it have the same no effect? – satanTime May 12 '20 at 12:39
  • @satanTime no effect, unfortunately! The problem is, it seems to work okay when js console is closed, but starts to lag when open! – Jm3s May 12 '20 at 12:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/213693/discussion-between-satantime-and-jm3s). – satanTime May 12 '20 at 12:40
2

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.

satanTime
  • 12,631
  • 1
  • 25
  • 73
0

For what it's worth - I ran into the same symptoms and it turned out that Chrome had an update pending in the background. So, if you also experience "The issue only really seems to happen when the chrome console is open" -- try to reboot Chrome to let the update complete. It worked for me.

Remus
  • 1,433
  • 3
  • 14
  • 24