You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:50:17