使用ng-template在cdk-virtual-scroll-viewport中渲染mat-table报错求助
报错信息
ERROR TypeError: Cannot read properties of undefined (reading '_switchDataSource')
at TableItemSizeDirective.ngAfterContentInit (ng-table-virtual-scroll.mjs:186:40)
当前代码结构
<cdk-virtual-scroll-viewport tvsItemSize [footerEnabled]="true" [headerEnabled]="false"> <ng-container *ngTemplateOutlet="operationTable;context:{$implicit:operationTableDataSource}"> </ng-container> </cdk-virtual-scroll-viewport> <ng-template #operationTable> <mat-table> ..... </mat-table> </ng-template>
正常运行的代码(不使用ng-template)
<cdk-virtual-scroll-viewport tvsItemSize [footerEnabled]="true" [headerEnabled]="false"> <mat-table> ..... </mat-table> </cdk-virtual-scroll-viewport>
问题原因与解决方案
这个错误源于ng-table-virtual-scroll的TableItemSizeDirective在ngAfterContentInit钩子执行时,会尝试获取viewport内部的mat-table实例,但ngTemplateOutlet属于延迟渲染逻辑——当指令初始化时,mat-table还未被插入到viewport的DOM树中,导致指令找不到table实例,进而调用_switchDataSource时出现undefined错误。
方案1:替换ngTemplateOutlet为ngIf(推荐)
若仅需复用或条件渲染table,用*ngIf替代ngTemplateOutlet,确保table在viewport初始化时就能被指令检测到:
<cdk-virtual-scroll-viewport tvsItemSize [footerEnabled]="true" [headerEnabled]="false"> <mat-table *ngIf="operationTableDataSource" [dataSource]="operationTableDataSource"> ..... </mat-table> </cdk-virtual-scroll-viewport>
方案2:手动触发指令初始化逻辑(需依赖库内部实现)
如果必须使用ngTemplateOutlet,可以通过ViewChild获取指令实例,在模板渲染完成后手动触发初始化:
- 组件内添加逻辑:
import { ViewChild, AfterViewInit } from '@angular/core'; import { TableItemSizeDirective } from 'ng-table-virtual-scroll'; @Component({...}) export class YourComponent implements AfterViewInit { @ViewChild(TableItemSizeDirective) tableItemSizeDir!: TableItemSizeDirective; ngAfterViewInit() { // 延迟执行确保模板渲染完成 setTimeout(() => { this.tableItemSizeDir?.ngAfterContentInit(); }); } }
- 保留原模板结构不变
方案3:调整内容投影时机
将ng-template内容直接嵌套在viewport内的ng-container中,同时确保数据源在viewport初始化前已准备好:
<cdk-virtual-scroll-viewport tvsItemSize [footerEnabled]="true" [headerEnabled]="false"> <ng-container> <mat-table [dataSource]="operationTableDataSource"> ..... </mat-table> </ng-container> </cdk-virtual-scroll-viewport>
总结
这不属于cdk-virtual-scroll的功能限制,而是ng-table-virtual-scroll库的指令对内容渲染时机有依赖——要求mat-table在指令的ngAfterContentInit钩子执行时已存在于viewport的内容投影中。ngTemplateOutlet的延迟渲染特性打破了这个依赖关系,从而引发报错。
内容的提问来源于stack exchange,提问作者Amit Chigadani

