Angular动态创建的表格组件无法保留状态,点击时总显示最后一个组件数据如何解决?
动态创建组件状态错乱问题的原因及解决方案
根因分析
- 引用对象共享导致值覆盖:JS中引用类型(对象、数组)的赋值是传址而非传值,如果你在循环生成多个单元格组件时,复用了同一个
data对象变量,仅修改其属性值传递给不同的动态组件,所有动态组件的instance.data最终会指向同一个内存地址,值为最后一次赋值的结果。初始渲染正常是因为同步渲染时刚好拿到当前循环的临时值,后续触发事件时读取的是引用指向的最终值。 - 异步生命周期赋值引发的覆盖:你将
tableData的赋值放在ngAfterViewInit中执行,该生命周期是异步触发的,所有单元格组件初始化完成后才会批量执行ngAfterViewInit,此时tableData如果是父组件传递的同一个引用变量,所有组件的data.tableData都会被覆盖为最后一次传入的值。 - (可选)ngFor无trackBy导致组件实例复用:如果父组件用
*ngFor循环渲染table-cell组件时没有设置唯一的trackBy函数,Angular会默认用索引作为追踪标识,当列表数据更新时会复用组件实例,导致后面的实例属性覆盖前面的实例。
修复步骤
- 首先补全
TableCellComponent缺失的输入属性定义:
@Component({ selector: 'table-cell', template: '<ng-template tableCellHost></ng-template>' }) export class TableCellComponent implements OnInit, OnDestroy, AfterViewInit { // 补上缺失的data输入属性 @Input() data: any; @Input() tableData: any[]; @ViewChild(TableCellDirective, { static: true }) tableCellHost: TableCellDirective; public componentRef: any; constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
- 切断引用传递,把
tableData赋值逻辑提前到ngOnInit中,删除原ngAfterViewInit的相关逻辑:
ngOnInit(): void { // 深拷贝当前传入的data,切断和外部变量的引用关系 const independentCellData = JSON.parse(JSON.stringify(this.data)); const tableCellItem = new TableCellItem(this.data.component, independentCellData); const componentFactory = this.componentFactoryResolver.resolveComponentFactory(tableCellItem.component); const viewContainerRef = this.tableCellHost.viewContainerRef; viewContainerRef.clear(); this.componentRef = viewContainerRef.createComponent<TableCellInterface>(componentFactory); // 赋值独立的data对象 this.componentRef.instance.data = tableCellItem.data; // 浅拷贝tableData切断引用共享,如果tableData是深层对象可以换成深拷贝 this.componentRef.instance.data.tableData = [...this.tableData]; // 手动触发一次变更检测,确保初始值正确渲染 this.componentRef.changeDetectorRef.detectChanges(); } // 删除原有ngAfterViewInit中的tableData赋值逻辑 ngAfterViewInit() {}
- 父组件
*ngFor循环渲染table-cell时添加trackBy函数,避免组件实例复用:
<!-- 父组件模板 --> <ng-container *ngFor="let cellConfig of cellList; trackBy: trackByCellKey"> <table-cell [data]="cellConfig.data" [tableData]="tableData" ></table-cell> </ng-container>
// 父组件类中添加trackBy方法 trackByCellKey(index: number, cellConfig: any) { // 返回每个单元格的唯一标识,不要用索引index return cellConfig.uniqueId; }
- (可选)如果你的动态组件内部的排序事件是通过
addEventListener手动绑定的,需要确保事件回调绑定了正确的this指向:
// 动态组件内部绑定事件示例 ngOnInit() { this.sortBtn.addEventListener('click', this.handleSort.bind(this)); }
内容的提问来源于stack exchange,提问作者Farnoosh
相关产品推荐
相关产品推荐

