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

Igx-Tree-Grid状态持久化:设置状态后单元格无法展开问题

IgxTreeGrid状态持久化后单元格展开失效问题

我在给应用添加状态持久化功能时,遇到IgxTreeGrid调用setState后单元格未按预期展开的问题。原本以为setState会自动处理树形行的展开状态,但完全没效果;尝试手动调用expandRow方法时,还触发了以下错误:

TypeError: Cannot read properties of undefined (reading 'expanded')
at _IgxTreeGridAPIService.allow_expansion_state_change (infragistics-igniteui-angular.mjs:111010:13)

已确认状态数据已正确存储选中行和展开行信息:

  • 单元格选择信息:
"cellSelection": [{
"rowStart": 8,
"rowEnd": 8,
"columnStart": 0,
"columnEnd": 0
}]
  • 展开信息:
"expansion": [[{
"code": 20,
"description": "STEELWORK",
"libraryId": "aff67ced-12aa-4b7b-a3af-08db8f460e7f",
"jointDisciplineId": null,
"schedules": [...]
}]]

组件代码

<igx-tree-grid igxGridState #disciplineSchedulesGrid (columnInit)="onDisciplineScheduleColumnInit($event)" [data]="disciplineList" [isLoading]="!disciplineList" [autoGenerate]="false"
  [allowFiltering]="true" [emptyGridTemplate]="emptyGrid" childDataKey="schedules" expansionDepth="0"
  [rowSelection]="'single'" [hideRowSelectors]="true"
  (rowSelectionChanging)="selectScheduleGridRow($event)">
    <igx-grid-toolbar>
      <igx-grid-toolbar-title>Rate Review</igx-grid-toolbar-title>

      <div *ngIf="bidderRates" style="display:flex">
        <igx-select #select name="bidderSelect" [placeholder]="'Please select Bidder'" (selectionChanging)="selectBidderId($event)" id="bidderSelect" class="vertical-centre">
          <igx-select-item *ngIf="!bidderList" [value]="null">
            No Bidders created
          </igx-select-item>
          <igx-select-item *ngFor="let bidder of bidderList" [value]="bidder.id">
            {{ bidder.bidderId }} <span *ngIf="bidder.name"> - {{ bidder.name }}</span>
          </igx-select-item>
        </igx-select>
      </div>

      <button type="button" id="btnAdd" igxButton="contained" igxRipple igxRippleCentered="true" (click)="gridStateComponent.showAddDialog(gridId, rateGridState)">Add State</button>

      <igx-select *ngIf="gridStateComponent" #select name="gridSelect" [(ngModel)]="gridStateComponent.selectedGridStateId" (selectionChanging)="gridStateComponent.selectGridStates($event)" id="gridSelect" class="vertical-centre" style="padding-left: 0;">
        <igx-select-item [value]="'default'" (click)="restoreGrid()">
          DEFAULT
        </igx-select-item>
        <igx-select-item *ngFor="let gridState of gridStateComponent.filteredGridStates" [value]="gridState.id">
          {{ gridState.stateName }}
        </igx-select-item>
      </igx-select>

      <button *ngIf="canDownload" #btnDownload id="btnDownload" igxButton="contained" igxRipple igxRippleCentered="true" (click)="startExcelDownload()" [disabled]="downloadRunning || (bidderRates && !bidderId)">
        <igx-icon name="download" style="padding-right: 1rem;"></igx-icon>Download Rates
      </button>
    </igx-grid-toolbar>
    <igx-column field="id" header="id" [editable]="false" [sortable]="false" [groupable]="false" [filterable]="false" [resizable]="true" [hidden]="true"></igx-column>
    <igx-column field="code" header="Disciplines & Schedules" [sortable]="true" [filterable]="true" [resizable]="true">
      <ng-template #disciplineScheduleCodeTemplate igxCell let-cell="cell">
        <span class="igx-grid__td" style="width: 100%">{{ cell.value | SORCode: disciplineCodeFormat }} - {{ cell.row.data.description }}</span>
      </ng-template>
    </igx-column>
</igx-tree-grid>

状态选择方法代码

selectGridStates(event: ISelectionEventArgs) {
    if (event.newSelection.value === "default") {
      this.selectedGridStateId = 'default';
      return;
    }

    this.selectedGridStateId = this.gridStates.filter(gs => gs.id == event.newSelection.value)[0].id;

    const stateName = this.filteredGridStates.filter(gs => gs.id === this.selectedGridStateId)[0].stateName;

    this.gridIds.forEach(gridId => {
      const state = this.gridStates.filter(gs => gs.gridId === gridId && gs.stateName === stateName)[0].state;
      this.gridStateDirectives[gridId].setState(JSON.parse(state));
      this.cdr.detectChanges();

          // Manually apply cell expansion state
      const expansionState = JSON.parse(state).expansion;
      if (expansionState) {
        expansionState.forEach(exp => {
          if (exp[1]) {
            const row = this.gridStateDirectives[gridId]?.grid?._dataView?.find(
              r => r.key?.id === exp[0].id);
           row.expanded = true;
            this.gridStateDirectives[gridId].grid.expandRow(row.id);
          }
        });
      }
    });
}

问题原因与修复方案

1. 状态存储格式错误

当前存储的expansion是完整行对象的嵌套数组,TreeGrid无法直接匹配这些数据来定位行。应该只存储行的唯一标识(比如libraryId或id),修改存储格式为:

"expansion": ["aff67ced-12aa-4b7b-a3af-08db8f460e7f"]

或者键值对格式(记录是否展开):

"expansion": {"aff67ced-12aa-4b7b-a3af-08db8f460e7f": true}

2. 手动展开时的行获取方式错误

不要直接操作内部属性_dataView,改用TreeGrid提供的getRowByKey方法获取行实例,同时延迟执行展开操作确保数据视图已更新:

selectGridStates(event: ISelectionEventArgs) {
    if (event.newSelection.value === "default") {
      this.selectedGridStateId = 'default';
      return;
    }

    // 简化查找逻辑,避免多次filter
    const targetState = this.gridStates.find(gs => gs.id === event.newSelection.value);
    if (!targetState) return;
    
    this.selectedGridStateId = targetState.id;
    const stateName = targetState.stateName;

    this.gridIds.forEach(gridId => {
      const gridState = this.gridStates.find(gs => gs.gridId === gridId && gs.stateName === stateName);
      if (!gridState) return;

      const parsedState = JSON.parse(gridState.state);
      this.gridStateDirectives[gridId].setState(parsedState);

      // 延迟执行,确保setState完成后数据视图稳定
      setTimeout(() => {
        const expansionState = parsedState.expansion;
        if (expansionState) {
          // 针对ID数组格式处理
          expansionState.forEach(rowKey => {
            const row = this.gridStateDirectives[gridId].grid.getRowByKey(rowKey);
            if (row) {
              this.gridStateDirectives[gridId].grid.expandRow(row.key);
            }
          });
          // 如果是键值对格式,替换为以下代码:
          // Object.entries(expansionState).forEach(([rowKey, isExpanded]) => {
          //   if (isExpanded) {
          //     const row = this.gridStateDirectives[gridId].grid.getRowByKey(rowKey);
          //     if (row) this.gridStateDirectives[gridId].grid.expandRow(row.key);
          //   }
          // });
        }
        this.cdr.detectChanges();
      }, 0);
    });
}

3. 确保igxGridState包含展开状态配置

在模板中明确指定igxGridState要包含的状态键,确保expansion被纳入持久化范围:

<igx-tree-grid 
  igxGridState="{ include: ['expansion', 'cellSelection', 'sorting', 'filtering'] }"
  #disciplineSchedulesGrid
  <!-- 其他属性保持不变 -->
>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:29:50