I have an angular code which is running fine, but I still do not have a concrete understanding of how change detection works.
The following component displays a table using material design and implements table pagination. I did the following steps:
- Set changeDetection to OnPush mode.
- Obtained a reference of the paginator from the template using @ViewChild(MatPaginator).
- Assigned the paginator to MatTableDataSource inside ngAfterViewInit using setTimeout after 3 seconds.
The result after initial page load is that the table paginator works fine after 3 seconds and the page is re-rendered correctly.
And this contradicts the fact that in OnPush mode, setTimeout should not trigger change detection and I am not changing the reference of any input property. Any thoughts?
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit, AfterViewInit {
dataSource: MatTableDataSource<IProduct> = new MatTableDataSource();
@ViewChild(MatPaginator) paginator!: MatPaginator;
displayedColumns: string[] = ['model', 'year', 'price'];
data: IProduct[] = [
{
model: 'Samsung-GT2',
year: 2022,
price: 120,
},
{
model: 'LG-X22',
year: 2021,
price: 99,
},
{
model: 'Philips-Q87',
year: 2019,
price: 69,
},
{
model: 'Huawei-SX1',
year: 2021,
price: 220,
},
];
constructor() {}
ngOnInit(): void {
this.dataSource.data = this.data;
}
ngAfterViewInit(): void {
setTimeout(() => {
this.dataSource.paginator = this.paginator;
}, 3000);
}
enter code here
CodePudding user response:
There's a few other causes for change detection with the OnPush
strategy, it is probably the MatTable
component that is triggering change detection on itself when the paginator
is changed.
A quote taken from here:
https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/
When using OnPush detectors, then the framework will check an OnPush component when any of its input properties changes, when it fires an event, or when an Observable fires an event
You would have to look at the source code of the mat table to find the exact cause, but as you can see it's difficult to control change detection of a pre-made component.
Every component has its own change detector, setting the parent to OnPush
will stop most change detections from permeating down to that child, but it will not stop the child triggering change detection on itself.
In the source code we see this note:
https://github.com/angular/components/blob/master/src/material/table/table.ts
// See note on CdkTable for explanation on why this uses the default change detection strategy.
// tslint:disable-next-line:validate-decorators
changeDetection: ChangeDetectionStrategy.Default
and going to the cdk table:
https://github.com/angular/components/blob/master/src/cdk/table/table.ts
// The "OnPush" status for the `MatTable` component is effectively a noop, so we are removing it.
// The view for `MatTable` consists entirely of templates declared in other views. As they are
// declared elsewhere, they are checked when their declaration points are checked.
// tslint:disable-next-line:validate-decorators
changeDetection: ChangeDetectionStrategy.Default,
And taking a look at the template:
export const CDK_TABLE_TEMPLATE =
// Note that according to MDN, the `caption` element has to be projected as the **first**
// element in the table. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption
`
<ng-content select="caption"></ng-content>
<ng-content select="colgroup, col"></ng-content>
<ng-container headerRowOutlet></ng-container>
<ng-container rowOutlet></ng-container>
<ng-container noDataRowOutlet></ng-container>
<ng-container footerRowOutlet></ng-container>
`;
Welp, looks like they're using directives to render multiple components, any of which could be triggering change detection.