-
html
<mat-tree [dataSource]="dataSource" [treeControl]="treeControl"> <mat-tree-node *matTreeNodeDef="let node" matTreeNodePadding [matTreeNodePaddingIndent]="10" (click)="choseNode(node)" [ngClass]="{ 'node--highlight': node.isChosen }" > <!-- 此为自定义的样式 组件;不用改成{{node.name}} 就好--> <app-custom-hover-node [node]="node" class="hover-row" (delNode)="delNode($event)" (editNode)="editNode($event)" (addNode)="addNode($event)" ></app-custom-hover-node> </mat-tree-node> <mat-tree-node *matTreeNodeDef="let node; when: hasChild" matTreeNodePadding [matTreeNodePaddingIndent]="20" (click)="choseNode(node)" [ngClass]="{ 'node--highlight': node.isChosen }" > <button mat-icon-button [attr.aria-label]="'Toggle ' + node.item" matTreeNodeToggle > <mat-icon class="mat-icon-rtl-mirror"> {{ treeControl.isExpanded(node) ? "expand_more" : "chevron_right" }} </mat-icon> </button> <app-custom-hover-node [node]="node" class="hover-row" (delNode)="delNode($event)" (editNode)="editNode($event)" (addNode)="addNode($event)" ></app-custom-hover-node> <mat-progress-bar *ngIf="node.isLoading" mode="indeterminate" class="example-tree-progress-bar" ></mat-progress-bar> </mat-tree-node> </mat-tree>
-
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<DynamicFlatNode> { // 定义了一个名为 DynamicDataSource 的类,它实现了 DataSource 接口,这个接口的泛型参数指定为 DynamicFlatNode,表示这个数据源提供的是 DynamicFlatNode 类型的数据。 dataChange = new BehaviorSubject<DynamicFlatNode[]>([]); //定义了一个名为 dataChange 的 BehaviorSubject 对象,用于在数据发生变化时向订阅者(subscribers)发送通 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<DynamicFlatNode>, 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<DynamicFlatNode[]> { // DataSource 接口中的 connect() 方法,这个方法返回一个 Observable 对象,它用于向 TreeControl 提供数据。 this._treeControl.expansionModel.changed.subscribe((change) => { if ( (change as SelectionChange<DynamicFlatNode>).added || (change as SelectionChange<DynamicFlatNode>).removed ) { this.handleTreeControl(change as SelectionChange<DynamicFlatNode>); } }); return merge(collectionViewer.viewChange, this.dataChange).pipe( map(() => this.data) ); } disconnect(collectionViewer: CollectionViewer): void {} // DataSource 接口中的 disconnect() 方法,这个方法在数据源不再需要使用时被调用,可以在这个方法中清理资源。 handleTreeControl(change: SelectionChange<DynamicFlatNode>) { // 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<DynamicFlatNode>( this.getLevel, this.isExpandable ); this.dataSource = new DynamicDataSource(this.treeControl, this.treeService); this.dataSource.initData(); //获得初始数据 } treeControl: FlatTreeControl<DynamicFlatNode>; //表示树形结构的控制器对象,用于管理树形结构的展开和闭合。 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%; } .custome-btn { line-height: 30px; height: 30px; width: 30px; } // 搜索框 :host ::ng-deep .mat-form-field{ width: 100%; } :host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-outline{ background: #F2F3F5; } :host ::ng-deep .mat-form-field-wrapper{ padding-bottom: 0.625rem; } :host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-flex{ margin-top: -1rem; height: 40px; } :host ::ng-deep .mat-form-field-infix{ border-top:8px solid transparent; }
自存放忘 angular 动态加载子节点
2301_789169542024-02-20 13:14
相关推荐
菜根Sec23 分钟前
XSS跨站脚本攻击漏洞练习m0_7482571830 分钟前
Spring Boot FileUpLoad and Interceptor(文件上传和拦截器,Web入门知识)桃园码工1 小时前
15_HTML5 表单属性 --[HTML5 API 学习之旅]百万蹄蹄向前冲1 小时前
2024不一样的VUE3期末考查轻口味2 小时前
【每日学点鸿蒙知识】AVCodec、SmartPerf工具、web组件加载、监听键盘的显示隐藏、Asset Store Kitalikami2 小时前
【若依】用 post 请求传 json 格式的数据下载文件wakangda2 小时前
React Native 集成原生Android功能吃杠碰小鸡2 小时前
lodash常用函数emoji1111113 小时前
前端对页面数据进行缓存泰伦闲鱼3 小时前
nestjs:GET REQUEST 缓存问题