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

Angular mat-tree使用Observable作为数据源丢失原始数据引用怎么办

问题原因

你直接将Observable对象传给CDK Tree的dataSource输入属性属于不符合API规范的用法,CDK Tree内部会自动将非DataSource类型的输入(包括Observable、普通数组)包装为内部的DataSource实现,这个包装过程会对源数据做深拷贝处理,因此模板中渲染得到的node对象是拷贝后的副本,和你原始TREE_DATA中的对象引用不一致,你用全等===判断自然无法匹配。

而使用ArrayDataSource的版本是直接传入原始数组的引用,内部没有额外的拷贝逻辑,所以引用比对可以正常通过。


解决方案

方案1:使用官方指定的嵌套树数据源(推荐)

使用Angular Material提供的MatTreeNestedDataSource来承接Observable返回的数据,直接将原始数据赋值给数据源的data属性即可保留引用:

import { MatTreeNestedDataSource } from '@angular/material/tree';

interface FoodNode {
  name: string;
  children?: FoodNode[];
}

const TREE_DATA: FoodNode[] = [
  {
    name: 'Fruit',
    children: [
      {name: 'Apple'},
      {name: 'Banana'},
      {name: 'Fruit loops'},
    ]
  }, {
    name: 'Vegetables',
    children: [
      {
        name: 'Green',
        children: [
          {name: 'Broccoli'},
          {name: 'Brussels sprouts'},
        ]
      }, {
        name: 'Orange',
        children: [
          {name: 'Pumpkins'},
          {name: 'Carrots'},
        ]
      },
    ]
  },
];

@Component({
  selector: 'cdk-tree-nested-example',
  templateUrl: 'cdk-tree-nested-example.html',
  styleUrls: ['cdk-tree-nested-example.css'],
})
export class CdkTreeNestedExample {
  treeControl = new NestedTreeControl<FoodNode>(node => node.children);
  dataSource = new MatTreeNestedDataSource<FoodNode>();
  // 自行维护原始数据的引用,用于后续查找、初始化选中状态
  private treeData = TREE_DATA;

  constructor() {
    // 模拟你的Observable数据源场景
    of(this.treeData).subscribe(data => {
      this.dataSource.data = data;
    });
  }

  hasChild = (_: number, node: FoodNode) => !!node.children && node.children.length > 0;

  testClick(node) {
    console.log(this.findNode(this.treeData, node));
  }

  findNode(data: FoodNode[] | FoodNode, node: FoodNode) {
    console.log('search')
    if(Array.isArray(data)) {
      return data.some(x => this.findNode(x, node));
    } else {
      return data === node || (data.children ? this.findNode(data.children, node) : false);
    }
  }
}

方案2:改用唯一标识匹配(彻底避免引用问题)

如果你的场景允许,建议给每个节点增加唯一ID字段,查找时用ID比对而非引用比对,彻底规避引用变化带来的问题,也更适合数据是从后端接口获取的场景:

interface FoodNode {
  id: number; // 新增唯一ID
  name: string;
  children?: FoodNode[];
}

// 查找逻辑修改为按ID匹配
findNode(data: FoodNode[] | FoodNode, targetNode: FoodNode) {
  if(Array.isArray(data)) {
    return data.some(x => this.findNode(x, targetNode));
  } else {
    return data.id === targetNode.id || (data.children ? this.findNode(data.children, targetNode) : false);
  }
}

SelectionModel初始化说明

调整完数据源后,直接从你自行维护的treeData中查找需要默认选中的节点,调用SelectionModel.select()方法即可,此时数据源和模板渲染的节点引用一致,选中状态可以正常生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 18:15:01