Angular递归组件解决多级数据结构问题

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、总结

  1. 观察数据结构,找出相同的数据结构,以便递归处理。
  2. 利用递归组件去递归数据并且展示。
  3. 用一个顶层的变量去存储当前递归组件的实例。
  4. 拿到各个实例,从而进行操作。
posted @ 2020-08-18 22:20  chenfengami  阅读(960)  评论(0编辑  收藏  举报