html
*matTreeNodeDef="let node" matTreeNodePadding [matTreeNodePaddingIndent]="10" (click)="choseNode(node)" [ngClass]="{ 'node--highlight': node.isChosen }" > [node]="node" class="hover-row" (delNode)="delNode($event)" (editNode)="editNode($event)" (addNode)="addNode($event)" >
*matTreeNodeDef="let node; when: hasChild" matTreeNodePadding [matTreeNodePaddingIndent]="20" (click)="choseNode(node)" [ngClass]="{ 'node--highlight': node.isChosen }" > [node]="node" class="hover-row" (delNode)="delNode($event)" (editNode)="editNode($event)" (addNode)="addNode($event)" > *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar" >
ts import { Component, OnInit, Injectable } from '@angular/core';
import {
CollectionViewer,
SelectionChange,
DataSource,
} from '@angular/cdk/collections'; //定义和操作数据源(DataSource)和视图容器(CollectionViewer)以及选择状态的变化(SelectionChange)
import { FlatTreeControl } from '@angular/cdk/tree'; //管理扁平节点的展开和折叠状态
import { BehaviorSubject, merge, Observable } from 'rxjs'; //创建可观察对象和处理异步数据流
import { map } from 'rxjs/operators'; // 从 RxJS 库中导入 map 操作符,用于对可观察对象发出的值进行转换。
import { TreeService } from './services/tree.service'; // 接口
// 树组件
export class DynamicFlatNode {
//定义了一个 DynamicFlatNode 类,表示一个扁平节点(Flat node),包含可展开状态和层级信息
constructor(
public item: string,
public level = 1,
public expandable = false,
public isLoading = false,
public isChosen = false
) {}
}
@Injectable({ providedIn: 'root' }) //提供服务的装饰器语法。它的作用是将这个服务注册为根注入器的提供者。
export class DynamicDataSource implements DataSource
// 定义了一个名为 DynamicDataSource 的类,它实现了 DataSource 接口,这个接口的泛型参数指定为 DynamicFlatNode,表示这个数据源提供的是 DynamicFlatNode 类型的数据。
dataChange = new BehaviorSubject
get data(): DynamicFlatNode[] {
//定义了一个名为 data 的 getter 方法,用于获取树形结构的数据。在这个方法中,通过访问 dataChange 的值来获取当前的树形结构数据。
return this.dataChange.value;
}
set data(value: DynamicFlatNode[]) {
//定义了一个名为 data 的 setter 方法,用于设置树形结构的数据。在这个方法中,首先将传入的新数据赋值给 _treeControl.dataNodes 属性,然后通过调用 dataChange.next() 方法发射新的数据,以通知所有订阅者更新数据。
this._treeControl.dataNodes = value;
this.dataChange.next(value);
}
toExpired = new DynamicFlatNode('');
constructor(
// 定义了一个构造函数,接受两个参数 _treeControl 和 _database,分别表示 TreeControl 对象和数据源对象。在这个构造函数中,将接收到的 _treeControl 对象赋值给组件的 _treeControl 属性,将接收到的 _database 对象赋值给私有属性 _database。
private _treeControl: FlatTreeControl
public treeService: TreeService
) {}
initData() {
this.treeService.getTree().subscribe((res) => {
this.data = (res || []).map(
(item: any) => new DynamicFlatNode(item.title, 0, true)
);
});
}
// 实现 DataSource 接口中的 connect() 和 disconnect() 方法
connect(collectionViewer: CollectionViewer): Observable
// DataSource 接口中的 connect() 方法,这个方法返回一个 Observable 对象,它用于向 TreeControl 提供数据。
this._treeControl.expansionModel.changed.subscribe((change) => {
if (
(change as SelectionChange
(change as SelectionChange
) {
this.handleTreeControl(change as SelectionChange
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(
map(() => this.data)
);
}
disconnect(collectionViewer: CollectionViewer): void {} // DataSource 接口中的 disconnect() 方法,这个方法在数据源不再需要使用时被调用,可以在这个方法中清理资源。
handleTreeControl(change: SelectionChange
// Tree中的一个方法,用于处理树形结构的控制变化
if (change.added) {
change.added.forEach((node) => this.toggleNode(node, true));
}
if (change.removed) {
change.removed
.slice()
.reverse()
.forEach((node) => this.toggleNode(node, false));
}
}
toggleNode(node: DynamicFlatNode, expand: boolean) {
// Tree中的一个方法,用于展开或关闭树形结构中的一个节点
const index = this.data.indexOf(node); //JavaScript 中数组的原生方法之一。这个方法用于返回数组中指定元素的索引,如果数组中不存在该元素,则返回 -1。
if (index < 0) {
// If no children, or cannot find the node, no op
return;
}
if (expand) {
// 根据 expand 参数的值来判断是展开节点还是折叠节点
node.isLoading = true;
this.treeService.getTree().subscribe((data) => {
const nodes = data.map(
(item: any) =>
new DynamicFlatNode(
item.title, //该节点的名字
node.level + 1, //级别
true //是否可以展开
) //通过 map 方法将子节点的名称转换为 DynamicFlatNode 对象,然后插入到当前节点的子节点列表中。
);
this.data.splice(index + 1, 0, ...nodes);
this.dataChange.next(this.data); //发出数据变化的通知,更新数据源
node.isLoading = false;
});
} else {
console.log('not expand');
let count = 0;
for (
let i = index + 1;
i < this.data.length && this.data[i].level > node.level;
i++, count++
) {}
this.data.splice(index + 1, count);
}
this.dataChange.next(this.data); //发出数据变化的通知,更新数据源
}
//删除节点
remove(node: DynamicFlatNode) {
const index = this.data.indexOf(node); JavaScript 中数组的原生方法之一。这个方法用于返回数组中指定元素的索引,如果数组中不存在该元素,则返回 -1。
if (index < 0) return;
let count = 1;
// 计算从该节点开始,需要删除的子节点个数 为什么是循环?因为data已经被打平了
for (
let i = index + 1;
i < this.data.length && this.data[i].level > node.level; //循环条件,当满足两个条件时继续循环:1:变量 i 不超过数组 this.data 的长度; 2:this.data[i].level > node.level说明该节点是被删除节点的子节点,需要被一并删除
i++, count++ // 每次循环结束后 做的操作
) {}
this.data.splice(index, count);
this.dataChange.next(this.data);
}
// 编辑节点
update(node: DynamicFlatNode, name: string) {
const index = this.data.indexOf(node); // 这行代码用于查找要更新的节点在数据源中的索引位置。
console.log('index: ', index);
if (index < 0) return;
this.data.splice(
index,
1,
new DynamicFlatNode(
name,
node.level,
node.expandable,
node.isLoading,
node.isChosen
)
);
this.dataChange.next(this.data);
}
// 新增节点
insert(parentNode: DynamicFlatNode, nodeName: string) {
if (!nodeName) return;
const index = this.data.indexOf(parentNode);
if (index < 0) return;
const child = new DynamicFlatNode(nodeName, parentNode.level + 1, true);
this.data.splice(index + 1, 0, child);
this.dataChange.next(this.data);
}
// 选择节点
choseNode(node: DynamicFlatNode) {
const index = this.data.indexOf(node);
if (index < 0) return;
this.data.forEach((item) => {
if (item.isChosen === true) {
item.isChosen = false;
return; // 终止循环
}
});
this.data.splice(
index,
1,
new DynamicFlatNode(
node.item,
node.level,
node.expandable,
node.isLoading,
true
)
);
this.dataChange.next(this.data);
}
}
@Component({
selector: 'app-dynamic-tree-demo',
templateUrl: './dynamic-tree-demo.component.html',
styleUrls: ['./dynamic-tree-demo.component.scss'],
}) //定义组件的装饰器要 紧挨着组件ts 要不要报错
export class DynamicTreeDemoComponent implements OnInit {
constructor(public treeService: TreeService) {
this.treeControl = new FlatTreeControl
this.getLevel,
this.isExpandable
);
this.dataSource = new DynamicDataSource(this.treeControl, this.treeService);
this.dataSource.initData(); //获得初始数据
}
treeControl: FlatTreeControl
dataSource: DynamicDataSource; //表示树形结构的数据源对象,用于管理树形结构的数据加载。
getLevel = (node: DynamicFlatNode) => node.level; //一个箭头函数,用于获取节点的级别。
isExpandable = (node: DynamicFlatNode) => node.expandable; //箭头函数,用于判断节点是否可展开。
delNode(node: any) {
this.dataSource.remove(node);
}
editNode(node: any) {
this.dataSource.update(node, '改变名字');
}
addNode(e: any) {
this.dataSource.insert(e[0], e[1]);
}
// 选择节点
choseNode(node: DynamicFlatNode) {
this.dataSource.choseNode(node);
}
hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable; //函数,用于判断节点是否有子节点。
ngOnInit(): void {}
}
scss // 树节点的高度
:host ::ng-deep .mat-tree-node {
max-height: 30px;
min-height: 30px;
border-radius: 3px;
}
// 树节点高亮
.node--highlight {
background: #ecf2fe;
color: #006eff;
}
// 自定义row宽度
.hover-row {
width: 100%;
}
精彩文章
发表评论