Angular学习笔记85:NG-ZORRO 树状表格惰性加载数据的实现

在使用 NG-ZORRO的树状表格时,官方提供的 demo是一次性加载全部数据的。在项目中,数据比较多的情况下,一次性获取全部数据,是非常影响用户体验的。所以实现树状表格惰性加载数据。

创建一个组件

$ ng g c sluggishness-tree-table
Your global Angular CLI version (10.2.0) is greater than your local
version (7.3.9). The local Angular CLI version is used.

To disable this warning use "ng config -g cli.warnings.versionMismatch false".
CREATE src/app/pages/sluggishness-tree-table/sluggishness-tree-table.component.less (0 bytes)
CREATE src/app/pages/sluggishness-tree-table/sluggishness-tree-table.component.html (42 bytes)
CREATE src/app/pages/sluggishness-tree-table/sluggishness-tree-table.component.spec.ts (735 bytes)
CREATE src/app/pages/sluggishness-tree-table/sluggishness-tree-table.component.ts (336 bytes)
UPDATE src/app/pages/pages.module.ts (3085 bytes)
(base) 

实现

  • 模版文件
<nz-table #expandTable
          [nzLoading]="loading"
          [nzData]="listOfMapData">
  <thead>
  <tr>
    <th nzWidth="40%">Name</th>
    <th nzWidth="30%">Age</th>
    <th>Address</th>
  </tr>
  </thead>
  <tbody>
  <ng-container *ngFor="let data of expandTable.data">
    <ng-container *ngFor="let item of mapOfExpandedData[data.key]">
      <tr *ngIf="(item.parent && item.parent.expand) || !item.parent">
        <td
          [nzIndentSize]="item.level * 20"
          [nzShowExpand]="!!item.children"
          [(nzExpand)]="item.expand"
          (nzExpandChange)="collapse(mapOfExpandedData[data.key], item, $event)">
          {{ item.name }}
        </td>
        <td>{{ item.age }}</td>
        <td>{{ item.address }}</td>
      </tr>
    </ng-container>
  </ng-container>
  </tbody>
</nz-table>

  • 类文件
import { Component, OnInit } from '@angular/core';
import { timer } from 'rxjs';

export interface TreeNodeInterface {
  key: number;
  name: string;
  age: number;
  level?: number;
  expand?: boolean;
  address: string;
  children?: TreeNodeInterface[];
}

@Component({
  selector: 'app-sluggishness-tree-table',
  templateUrl: './sluggishness-tree-table.component.html',
  styleUrls: ['./sluggishness-tree-table.component.less']
})

export class SluggishnessTreeTableComponent implements OnInit {
  public loading = false;
  public listOfMapData = [
    {
      key: 1,
      name: 'John Brown sr.',
      age: 60,
      address: 'New York No. 1 Lake Park',
      children: []
    },
    {
      key: 2,
      name: 'Joe Black',
      age: 32,
      address: 'Sidney No. 1 Lake Park',
      children: []
    }
  ];
  mapOfExpandedData: { [key: string]: TreeNodeInterface[] } = {};

  constructor() {
  }

  ngOnInit(): void {
    this.listOfMapData.forEach(item => {
      this.mapOfExpandedData[item.key] = this.convertTreeToList(item);
    });
  }

  collapse(array: TreeNodeInterface[], data: TreeNodeInterface, $event: boolean): void {
    if ($event === false) {
      if (data.children) {
        data.children.forEach(d => {
          const target = array.find(a => a.key === d.key)!;
          target.expand = false;
          this.collapse(array, target, false);
        });
      } else {
        return;
      }
    }
    if ($event) {
      if (data.children.length === 0) {
        this.loading = true;
        timer(1000).subscribe(() => {
          this.loading = false;
          const childData: TreeNodeInterface[] = [
            {
              key: Math.random(),
              name: `John Brown${Math.random()}`,
              age: 42,
              address: 'New York No. 2 Lake Park',
              children: []
            },
            {
              key: Math.random(),
              name: `John Brown jr.${Math.random()}`,
              age: 30,
              address: 'New York No. 3 Lake Park',
              children: []
            }
          ];
          data.children = childData;
          Array.from(childData).forEach((child) => {
            const childObj = {
              ...child,
              level: data.level + 1,
              expand: false,
              parent: data,
              children: []
            };
            // 插入到具体的节点中
            if (!array.map(opt => opt.key).includes(child.key)) {
              const childParentIndex = array.map(opt => opt.key).indexOf(data.key);
              array.splice(childParentIndex + 1, 0, childObj);
            }
          });
        });
      }

    }
  }

  convertTreeToList(root: object): TreeNodeInterface[] {
    const stack: any[] = [];
    const array: any[] = [];
    const hashMap = {};
    stack.push({ ...root, level: 0, expand: false });

    while (stack.length !== 0) {
      const node = stack.pop();
      this.visitNode(node, hashMap, array);
      if (node.children) {
        for (let i = node.children.length - 1; i >= 0; i--) {
          stack.push({ ...node.children[i], level: node.level + 1, expand: false, parent: node });
        }
      }
    }

    return array;
  }

  visitNode(node: TreeNodeInterface, hashMap: { [key: string]: any }, array: TreeNodeInterface[]): void {
    if (!hashMap[node.key]) {
      hashMap[node.key] = true;
      array.push(node);
    }
  }

}

  • 效果图

效果图

具体实现

这里大部分代码其实和 NG-ZORRO给出的demo差不多,重点的代码就是 nzExpandChange的回调函数 collapse的实现

 collapse(array: TreeNodeInterface[], data: TreeNodeInterface, $event: boolean): void {
    if ($event === false) {
      if (data.children) {
        data.children.forEach(d => {
          const target = array.find(a => a.key === d.key)!;
          target.expand = false;
          this.collapse(array, target, false);
        });
      } else {
        return;
      }
    }
    if ($event && data.children.length === 0) {
      this.loading = true;
      timer(1000).subscribe(() => {
        this.loading = false;
        const childData: TreeNodeInterface[] = [
          {
            key: Math.random(),
            name: `John Brown${Math.random()}`,
            age: 42,
            address: 'New York No. 2 Lake Park',
            children: []
          },
          {
            key: Math.random(),
            name: `John Brown jr.${Math.random()}`,
            age: 30,
            address: 'New York No. 3 Lake Park',
            children: []
          }
        ];
        // 将获取的值赋值给当前这个item
        data.children = childData;
        Array.from(childData).forEach((child) => {
          const childObj = {
            ...child,
            level: data.level + 1,
            expand: false,
            parent: data,
            children: []
          };
          // 插入到具体的节点中
          if (!array.map(opt => opt.key).includes(child.key)) {
            const childParentIndex = array.map(opt => opt.key).indexOf(data.key);
            array.splice(childParentIndex + 1, 0, childObj);
          }
        });
      });
    }
  }

这里$eventfalse时,树折叠起来,这里可以不用处理,增加一个$event===true的时候的逻辑。
使用timer(1000)设置一个超时函数来模拟http请求,childData模拟时远端服务器返回的数据。

  • 代码
data.children = childData;

将获取到的数据赋值给当前元素的 children

  • 代码
Array.from(childData).forEach((child) => {
          const childObj = {
            ...child,
            level: data.level + 1,
            expand: false,
            parent: data,
            children: []
          };
          // 插入到具体的节点中
          if (!array.map(opt => opt.key).includes(child.key)) {
            const childParentIndex = array.map(opt => opt.key).indexOf(data.key);
            array.splice(childParentIndex + 1, 0, childObj);
          }
        });

为获取到的每一个元素设置levelexpandparentchildren字段。然后将这个元素插入到对应的节点中。

需要注意

  • 数据中要存在主键

数据中要有一个字段是作为主键存在的,不能重复,当前的demo中为key字段,这个主键字段在转成map以后作为mapkey存在。所以这里模拟后端取的数据中,数据中key字段的值是取随机数。


详细代码已经上传到GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiayucn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值