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

Angular 2+Angular Material双向表格行迁移报错:无法读取undefined的'sdeptname'

解决Angular Material表格行迁移时的全量删除与属性报错问题

看起来你遇到的问题大概率是选中状态管理混乱和数据源更新方式不当导致的,我来帮你拆解并修复这个问题:

问题根源分析

  1. 共用SelectionModel:如果两个表格使用同一个选中模型,会导致选中状态互相干扰,点击迁移时可能误判选中了所有行,从而清空整个表格。
  2. 数据源更新不规范:直接修改原数组(比如splice)而不是创建新数组,Angular的变更检测可能无法识别变化,导致表格渲染异常,甚至出现undefined对象引发属性读取报错。
  3. 未过滤无效选中项:选中模型中可能混入了undefined行,迁移后模板绑定row.sdeptname时自然会报错。

修复后的完整代码示例

组件逻辑(.ts文件)

import { Component } from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { SelectionModel } from '@angular/material/checkbox';

// 定义数据接口,确保类型安全
interface Department {
  id: number;
  sdeptname: string;
}

@Component({
  selector: 'app-table-transfer',
  templateUrl: './table-transfer.component.html',
  styleUrls: ['./table-transfer.component.css']
})
export class TableTransferComponent {
  displayedColumns: string[] = ['select', 'id', 'sdeptname'];
  
  // 两个表格的独立数据源
  dataSource1 = new MatTableDataSource<Department>([
    {id: 1, sdeptname: '财务部'},
    {id: 2, sdeptname: '技术部'},
    {id: 3, sdeptname: '人事部'}
  ]);
  dataSource2 = new MatTableDataSource<Department>([]);
  
  // 关键:给每个表格分配独立的选中模型,避免状态冲突
  selection1 = new SelectionModel<Department>(true, []);
  selection2 = new SelectionModel<Department>(true, []);

  // 移至表格2逻辑
  moveToTable2() {
    // 过滤掉可能的undefined选中项
    const selectedRows = this.selection1.selected.filter(row => row);
    if (selectedRows.length === 0) return;

    // 用扩展运算符创建新数组,触发Angular变更检测
    this.dataSource2.data = [...this.dataSource2.data, ...selectedRows];
    // 从表格1移除选中行
    this.dataSource1.data = this.dataSource1.data.filter(row => !this.selection1.isSelected(row));
    
    // 清空表格1的选中状态
    this.selection1.clear();
  }

  // 移至表格1逻辑
  moveToTable1() {
    const selectedRows = this.selection2.selected.filter(row => row);
    if (selectedRows.length === 0) return;

    this.dataSource1.data = [...this.dataSource1.data, ...selectedRows];
    this.dataSource2.data = this.dataSource2.data.filter(row => !this.selection2.isSelected(row));
    
    this.selection2.clear();
  }

  // 表格1的全选判断
  isAllSelected1() {
    const numSelected = this.selection1.selected.length;
    const numRows = this.dataSource1.data.length;
    return numSelected === numRows;
  }

  // 表格1的全选切换
  toggleAllRows1() {
    this.isAllSelected1() ? this.selection1.clear() : this.selection1.select(...this.dataSource1.data);
  }

  // 表格2的全选判断
  isAllSelected2() {
    const numSelected = this.selection2.selected.length;
    const numRows = this.dataSource2.data.length;
    return numSelected === numRows;
  }

  // 表格2的全选切换
  toggleAllRows2() {
    this.isAllSelected2() ? this.selection2.clear() : this.selection2.select(...this.dataSource2.data);
  }
}

模板文件(.html文件)

<div class="table-container">
  <h3>表格1</h3>
  <button mat-raised-button color="primary" (click)="moveToTable2()">移至表格2</button>
  <table mat-table [dataSource]="dataSource1" class="mat-elevation-z8">
    <!-- 选择列 -->
    <ng-container matColumnDef="select">
      <th mat-header-cell *matHeaderCellDef>
        <mat-checkbox 
          (change)="$event ? toggleAllRows1() : null"
          [checked]="selection1.hasValue() && isAllSelected1()"
          [indeterminate]="selection1.hasValue() && !isAllSelected1()">
        </mat-checkbox>
      </th>
      <td mat-cell *matCellDef="let row">
        <mat-checkbox 
          (click)="$event.stopPropagation()"
          (change)="$event ? selection1.toggle(row) : null"
          [checked]="selection1.isSelected(row)">
        </mat-checkbox>
      </td>
    </ng-container>

    <!-- ID列 -->
    <ng-container matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef> ID </th>
      <td mat-cell *matCellDef="let row"> {{row.id}} </td>
    </ng-container>

    <!-- 部门名列 -->
    <ng-container matColumnDef="sdeptname">
      <th mat-header-cell *matHeaderCellDef> 部门名称 </th>
      <td mat-cell *matCellDef="let row"> {{row.sdeptname}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

<div class="table-container">
  <h3>表格2</h3>
  <button mat-raised-button color="primary" (click)="moveToTable1()">移至表格1</button>
  <table mat-table [dataSource]="dataSource2" class="mat-elevation-z8">
    <!-- 选择列 -->
    <ng-container matColumnDef="select">
      <th mat-header-cell *matHeaderCellDef>
        <mat-checkbox 
          (change)="$event ? toggleAllRows2() : null"
          [checked]="selection2.hasValue() && isAllSelected2()"
          [indeterminate]="selection2.hasValue() && !isAllSelected2()">
        </mat-checkbox>
      </th>
      <td mat-cell *matCellDef="let row">
        <mat-checkbox 
          (click)="$event.stopPropagation()"
          (change)="$event ? selection2.toggle(row) : null"
          [checked]="selection2.isSelected(row)">
        </mat-checkbox>
      </td>
    </ng-container>

    <!-- ID列 -->
    <ng-container matColumnDef="id">
      <th mat-header-cell *matHeaderCellDef> ID </th>
      <td mat-cell *matCellDef="let row"> {{row.id}} </td>
    </ng-container>

    <!-- 部门名列 -->
    <ng-container matColumnDef="sdeptname">
      <th mat-header-cell *matHeaderCellDef> 部门名称 </th>
      <td mat-cell *matCellDef="let row"> {{row.sdeptname}} </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

核心修复点说明

  1. 独立选中模型:给每个表格单独创建SelectionModel,彻底避免选中状态互相干扰。
  2. 规范数据源更新:始终通过创建新数组(扩展运算符[...]、filter)的方式更新dataSource.data,确保Angular能正确检测到数据变化并刷新表格。
  3. 过滤无效选中项:在迁移前过滤掉选中模型中可能存在的undefined行,防止模板绑定报错。

内容的提问来源于stack exchange,提问作者Heena

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:17:26