Angular中array.push(row)无法被EventEmitter检测到变更的问题求助
问题:EventEmitter仅首次选中/取消选中行时生效
我遇到一个问题:EventEmitter只有在首次选中行和取消选中行时生效。我知道原因是this.selectedRows.push(row)的变更无法被EventEmitter检测到,虽然重新过滤行并重新赋值给selectedRows能解决问题,但这不是合理方案。请解释原因并给出正确解决方案?
ParentComponent.html
<ng-container matColumnDef="select"> <th mat-header-cell *matHeaderCellDef="let table" [resizeColumn]="true" [index]="i"> <mat-checkbox (change)="$event ? masterToggle(supplier.invoice) : null" [checked]="selection.hasValue() && isAllSelected(supplier.invoice)" [indeterminate]="selection.hasValue() && !isAllSelected(supplier.invoice)" [aria-label]="checkboxLabel()"> </mat-checkbox> </th> <td mat-cell *matCellDef="let row"> <mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? toggle($event, row) : null" (change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)" [aria-label]="checkboxLabel(row, supplier.invoice)"> </mat-checkbox> </td> </ng-container>
ParentComponent.ts
toggle($event, row) { console.log("=====================",this.selectedRows); if($event.checked) { if (this.selectedRows.indexOf(row) === -1) { console.log("======================", "Checked"); this.selectedRows.push(row); this.selectedData.emit(this.selectedRows); } } else { const index = this.selectedRows.indexOf(row); if (index > -1) { this.selectedRows = this.selectedRows.filter(obj => obj !== row); this.selectedData.emit(this.selectedRows); } } }
原因分析
Angular的变更检测和事件订阅逻辑依赖引用变化来感知数据更新。数组的push方法属于原地修改,只会改变数组内容但不会修改数组本身的内存引用地址。虽然你手动调用了emit,但如果订阅者依赖Angular变更检测来响应数据变化,原地修改的数组无法触发后续的更新逻辑,导致只有首次变更能被感知。
另外你的代码存在一个额外问题:同一个<mat-checkbox>绑定了两个(change)事件,这会导致事件触发顺序不可控,可能打乱selection和selectedRows的同步逻辑。
正确解决方案
方案1:创建新数组引用(符合Angular规范)
添加元素时不要用push,而是通过扩展运算符创建新数组,主动改变引用地址,让变更检测和EventEmitter订阅者都能感知到变化:
toggle($event, row) { console.log("=====================", this.selectedRows); if ($event.checked) { if (this.selectedRows.indexOf(row) === -1) { console.log("======================", "Checked"); // 创建新数组替换原引用 this.selectedRows = [...this.selectedRows, row]; this.selectedData.emit(this.selectedRows); } } else { const index = this.selectedRows.indexOf(row); if (index > -1) { this.selectedRows = this.selectedRows.filter(obj => obj !== row); this.selectedData.emit(this.selectedRows); } } }
方案2:修复重复事件绑定
合并<mat-checkbox>上的两个(change)事件,避免逻辑冲突:
<td mat-cell *matCellDef="let row"> <mat-checkbox (click)="$event.stopPropagation()" (change)="handleRowToggle($event, row)" [checked]="selection.isSelected(row)" [aria-label]="checkboxLabel(row, supplier.invoice)"> </mat-checkbox> </td>
对应TS逻辑合并:
handleRowToggle($event, row) { if ($event.checked) { this.selection.select(row); if (this.selectedRows.indexOf(row) === -1) { this.selectedRows = [...this.selectedRows, row]; } } else { this.selection.deselect(row); this.selectedRows = this.selectedRows.filter(obj => obj !== row); } this.selectedData.emit(this.selectedRows); }
方案3:利用MatTable的SelectionModel(推荐)
既然已经使用Angular Material的SelectionModel,可以直接基于它的订阅机制同步数据,无需手动维护数组:
// 初始化SelectionModel selection = new SelectionModel<any>(true, []); ngOnInit() { // 订阅选中项变化,自动同步并emit this.selection.changed.subscribe(() => { this.selectedRows = this.selection.selected; this.selectedData.emit(this.selectedRows); }); } // 简化toggle方法 toggle($event, row) { this.selection.toggle(row); }
这种方式既避免了手动维护数组的冗余代码,又能保证数据同步和事件触发的准确性。
内容的提问来源于stack exchange,提问作者Mad
相关产品推荐
相关产品推荐

