Angular 6 MatTable Performance in 1000 rows
Not sure if this will help your situation as there's no code but we've found that the MatTable loads very slowly if a large data set is set before you set the datasource paginator.
For example - this takes several seconds to render...
dataSource: MatTableDataSource<LocationItem> = new MatTableDataSource();
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
ngOnInit() {
this.dataSource.data = [GetLargeDataSet];
}
ngAfterViewInit() {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
...but this is fast
ngOnInit() {
// data loaded after view init
}
ngAfterViewInit() {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
/* now it's okay to set large data source... */
this.dataSource.data = [GetLargeDataSet];
}
Incidentally, we were only finding this issue the second time we access the component as the large dataset from server was being cached and was immediately available the second time component was accessed. Another option is to add .delay(100) to your observable if you want to leave that code in the ngOnInit function.
Anyway, this may or may not help your situation.
To extend upons @Turneye 's answer. The reason it's happening is because the paginator is being set after all the rows have been rendered, because that's what the ngAfterViewInit
hook tells you.
So it first renders all rows from data source, then it sees: "hey, I'm getting a paginator, let me just remove all the rows (except the pager count)". This is obviously very slow on large data sets and/or complex table cell templates.
You can solve this by using the {static: true}
option on your @ViewChild
. This will make the paginator available inside the ngOnInit
lifecycle hook, and before any row has been rendered:
To steal his code, you can change it to this, and still have a fast table:
readonly dataSource: MatTableDataSource<LocationItem> = new MatTableDataSource();
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
ngOnInit() {
this.dataSource.data = [ GetLargeDataSet ];
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
}
Be aware though that this will not work if your mat-paginator
is inside a structural directive like *ngIf
Another performance issue could be that you have to declare a trackBy
function:
To improve performance, a trackBy function can be provided to the table similar to Angular’s ngFor trackBy. This informs the table how to uniquely identify rows to track how the data changes with each update.
<table mat-table [dataSource]="dataSource" [trackBy]="myTrackById">
Large DataSet Observable Passed Down From Parent Component
After much struggling, I was able to combine pieces of many different answers from this post, including @turneye's above and the OP's selected right answer, and also answers on another similar SO post, here and here.
I had ~1000 rows that I needed for a parent component on the page, as well as several child components, including a paginated MatTable
component.
The data was loading in <500ms, but then the page would take, on average, 15 seconds to render, as originally I was just passing a MatTableDataSource
object with the data already assigned.
The solution:
- Pass an observable with the data, and then subscribe to it after the view initializes, setting the
MatTableDataSource.data
property after setting theMatPaginator
andMatSort
. - Set
changeDetection
toChangeDetectionStrategy.OnPush
in theComponent
decorator config. - After setting the
MatDataSource.data
property in the observable body, tell angular to detect changes withChangeDetectorRef.detectChanges()
Now the full DOM is rendered in ~1 second total, which is fine given the volume of data that needs to be present at once.
Here's a stripped down example:
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-dynamic-grid',
templateUrl: './dynamic-grid.component.html',
styleUrls: ['./dynamic-grid.component.scss'],
})
export class DynamicGridComponent implements OnInit, AfterViewInit {
@Input() public dataSource$: Observable<any[]>;
public matDataSource = new MatTableDataSource<any>();
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
constructor(private changeDetectorRef: ChangeDetectorRef) {}
ngOnInit(): void {}
ngAfterViewInit() {
this.matDataSource.paginator = this.paginator;
this.matDataSource.sort = this.sort;
this.dataSource$.subscribe(x => {
this.matDataSource.data = x;
// The important part:
this.changeDetectorRef.detectChanges();
});
}
}
I had solved this issue and I improved the performance by wrapping the table in custom (grid) component and Control the changeDetection
of the component to be ChangeDetectionStrategy.OnPush
and when I want to render update I used ChangeDetectorRef.detectChanges()