Angular12调用CdkTable的renderRows时报undefined属性读取错误如何解决
问题原因
你遇到的Cannot read property 'renderRows' of undefined错误,核心原因是Angular的@ViewChild查询结果在ngOnInit生命周期阶段还未完成初始化。ngOnInit触发时组件的DOM模板还没有完成渲染挂载,此时通过@ViewChild查询到的DOM/组件实例仍然是undefined,访问其属性自然会触发报错。
另外你代码中CdkTable<string>的泛型定义和实际数据源类型PeriodicElement不匹配,也可能后续引发类型相关报错。
修复步骤
- 将操作cdkTable实例的逻辑从
ngOnInit迁移到ngAfterViewInit生命周期钩子中,该钩子会在组件视图完全渲染完成后触发,此时@ViewChild的查询结果已经完成赋值。 - 检查组件模板代码,确认
<cdk-table>标签上添加了#cdkTable模板引用变量,否则@ViewChild无法匹配到对应的cdkTable实例。 - 修正CdkTable的泛型定义,和你的数据源类型保持一致。
可选说明:如果有特殊场景需要在ngOnInit阶段访问cdkTable实例,且你的cdkTable在模板中不是被*ngIf等条件渲染控制的,可以修改@ViewChild配置为@ViewChild('cdkTable', {static: true}) cdkTable!: CdkTable<PeriodicElement>;,这种情况下查询结果会在ngOnInit前完成初始化。不过更推荐优先使用ngAfterViewInit方案,适用场景更广。
修改后的核心代码示例
// 首先导入AfterViewInit接口 import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import {DataSource} from '@angular/cdk/collections'; import {BehaviorSubject, Observable} from 'rxjs'; import { CdkTable } from '@angular/cdk/table'; export interface PeriodicElement { name: string; position: number; weight: number; symbol: string; } const ELEMENT_DATA: PeriodicElement[] = [ {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'}, {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'}, {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'}, {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'}, {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'}, {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'}, {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'}, {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'}, {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'}, {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'}, ]; @Component({ selector: 'app-cdk-table-basic-example', templateUrl: './cdk-table-basic-example.component.html', styleUrls: ['./cdk-table-basic-example.component.css'] }) export class CdkTableBasicExampleComponent implements OnInit, AfterViewInit { // 修正泛型为PeriodicElement @ViewChild('cdkTable') cdkTable!: CdkTable<PeriodicElement>; @ViewChild('cdkTable', {read: ElementRef}) cdkTableElement!: ElementRef; constructor() { } ngOnInit(): void { // 原cdkTable相关逻辑移除 } ngAfterViewInit(): void { // 把原逻辑移到此处,增加bind避免this指向异常 const org_renderRows = this.cdkTable.renderRows.bind(this.cdkTable); this.cdkTable.renderRows = () => { console.debug('#renderRows'); org_renderRows(); const rows = this.cdkTable._getRenderedRows(this.cdkTable._rowOutlet); console.debug('#renderRows - rows count: ' + rows.length); if (rows.length > 0) { console.debug('Scroll to the End is ON - auto-scroll down to latest metric'); rows[rows.length - 1].scrollIntoView(false); } else { console.debug('Scroll to the End is OFF - don\'t auto-scroll'); } }; } displayedColumns: string[] = ['position', 'name', 'weight', 'symbol']; dataSource = new ExampleDataSource(); } export class ExampleDataSource extends DataSource<PeriodicElement> { data = new BehaviorSubject<PeriodicElement[]>(ELEMENT_DATA); connect(): Observable<PeriodicElement[]> { return this.data; } disconnect() {} }
内容的提问来源于stack exchange,提问作者vrreddy1234
相关产品推荐
相关产品推荐

