Angular递归组件解决多级数据结构问题
前言
在碰到树状结构的数据渲染时,往往层级的深度是不确定的,遇到这些问题,通常是用到了递归的方法去解决这些问题,而相对于一个组件来说,就可以通俗的说成递归组件。
1、数据结构如下
[
{
"id": 1,
"name": "总公司",
"parentid": 0,
"order": 100000000,
"checked": true,
"level": 1,
"expanded": false,
"children": [
{
"id": 2,
"name": "线上直销",
"parentid": 1,
"order": 100000000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 3,
"name": "线下直销",
"parentid": 1,
"order": 99999000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 4,
"name": "客服",
"parentid": 1,
"order": 99998000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 5,
"name": "渠道",
"parentid": 1,
"order": 99997000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 6,
"name": "产品",
"parentid": 1,
"order": 99996000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 7,
"name": "技术",
"parentid": 1,
"order": 99995000,
"checked": 0,
"level": 2,
"expanded": false,
"children": [
{
"id": 12,
"name": "测试",
"parentid": 7,
"order": 100000000,
"checked": 0,
"level": 3,
"expanded": false,
"children": []
},
{
"id": 13,
"name": "开发",
"parentid": 7,
"order": 99999000,
"checked": 0,
"level": 3,
"expanded": false,
"children": []
},
{
"id": 14,
"name": "测试很长的名称显示",
"parentid": 7,
"order": 99998000,
"checked": 0,
"level": 3,
"expanded": false,
"children": [
{
"id": 15,
"name": "测试很长的名称显示2",
"parentid": 14,
"order": 100000000,
"checked": 0,
"level": 4,
"expanded": false,
"children": []
}
]
}
]
},
{
"id": 8,
"name": "运营",
"parentid": 1,
"order": 99994000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 9,
"name": "市场推广",
"parentid": 1,
"order": 99993000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 10,
"name": "讲师",
"parentid": 1,
"order": 99992000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
},
{
"id": 11,
"name": "商务",
"parentid": 1,
"order": 99991000,
"checked": 0,
"level": 2,
"expanded": false,
"children": []
}
]
}
]
2、使用递归组件去递归数据并且展示
观察我们可以发现,每一条数据的结构都是一样的,有下级的关系children字段是有值的,因此书写一个结点组件department-tree-node
。
当我们有子结点的时候,递归department-tree-node
组件去实现数据的展示。
<div class="department-tree-node">
<div class="tree-node-wrap" *ngFor="let item of nodes">
<div class="directory-wrap" [style.marginLeft]="item.children && item.children.length ? '0px' : '12px'">
<i class="anticon anticon-caret-right close"
*ngIf="item.children && item.children.length && !item.expanded"
(click)="toggleExpand(item)">
</i>
<i class="anticon anticon-caret-down expand"
*ngIf="item.children && item.children && item.expanded"
(click)="toggleExpand(item)">
</i>
<p class="title-wrap" [class.checked]="item.checked" (click)="selectedGroup(item)">
<i class="anticon anticon-folder"></i>
<span class="title">{{ item.name }}</span>
</p>
</div>
<ng-container *ngIf="item.children && item.children.length">
<div class="child-directory-wrap" [hidden]="!item.expanded">
<department-tree-node [nodes]="item.children"></department-tree-node>
</div>
</ng-container>
</div>
</div>
3、递归组件数据管理
因为递归组件中,数据并非全部都在一个组件的属性变量里头,我们通常需要在组件初始化的时候去把这个组件给保存起来,以便后续去管理对应组件中的对应数据。如我们传入的Input
为结点nodes数据,我们需要在点击一个子结点的时候去选中这个结点,并且给上class="checked"的样式,然后要把其他选中的结点给取消这个状态。这时我们访问this.nodes
的时候,只有传入的nodes数据,并非全部的数据,因为所有的组件都是单例的,this
指向都是指向当前的实例,因此this.nodes
只是访问当前实例中的nodes这个数据而已,为了解决这个问题,我们可以在ngOnint
这个生命周期里面,在模块中注入一个模块级别的service
以便存入当前的组件实例。
/**
* @file 解决递归组件 实例指向问题 用service直接穿透
*/
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class MemberService {
// 各个递归组件的实例
public nodesInstance = [];
}
/**
* @file 部门树形结构
*/
import { Component, Input, OnInit } from '@angular/core';
import { MemberService } from '../../member.service';
@Component({
selector: 'department-tree-node',
templateUrl: './department-tree-node.component.html',
styleUrls: ['./department-tree-node.component.scss']
})
export class DepartmentTreeNodeComponent implements OnInit {
@Input()
public nodes = [];
constructor(
public memberService: MemberService
) { }
public ngOnInit() {
// 将每一个组件的实例存储
this.memberService.nodesInstance.push(this);
}
public toggleExpand(item: any) {
item.expanded = !item.expanded;
}
public selectedGroup(item: any) {
// 遍历组件实例中的结点数据nodes
this.memberService.nodesInstance.forEach((instance: DepartmentTreeNodeComponent) => {
instance.toggleChecked(instance, instance.nodes);
});
item.checked = true;
this.memberService.departmentIdBehaviorSubject.next(item.id);
}
public toggleChecked(instance: DepartmentTreeNodeComponent, nodes: Array<object>) {
nodes.forEach((item: any) => {
item.checked = false;
if (item.children && item.children.length) {
instance.toggleChecked(instance, item.children);
}
});
}
}
4、总结
- 观察数据结构,找出相同的数据结构,以便递归处理。
- 利用递归组件去递归数据并且展示。
- 用一个顶层的变量去存储当前递归组件的实例。
- 拿到各个实例,从而进行操作。