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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
treegrid插件 当前选中的行: var config = { id: "tg1", width: "800", renderTo: "div1", headerAlign: "left", headerHeight: "30", dataAlign: "left", indentation: "20", folderOpenIcon: "images/folderOpen.gif", folderCloseIcon: "images/folderClose.gif", defaultLeafIcon: "images/defaultLeaf.gif", hoverRowBackground: "false", folderColumnIndex: "1", itemClick: "itemClickEvent", columns:[ {headerText: "", headerAlign: "center", dataAlign: "center", width: "20", handler: "customCheckBox"}, {headerText: "名称", dataField: "name", headerAlign: "center", handler: "customOrgName"}, {headerText: "拼音码", dataField: "code", headerAlign: "center", dataAlign: "center", width: "100"}, {headerText: "负责人", dataField: "assignee", headerAlign: "center", dataAlign: "center", width: "100"}, {headerText: "查看", headerAlign: "center", dataAlign: "center", width: "50", handler: "customLook"} ], data:[ {name: "城区分公司", code: "CQ", assignee: "", children:[ {name: "城区卡品分销中心"}, {name: "先锋服务厅", children:[ {name: "chlid1"}, {name: "chlid2"}, {name: "chlid3", children: [ {name: "chlid3-1"}, {name: "chlid3-2"}, {name: "chlid3-3"}, {name: "chlid3-4"} ]} ]}, {name: "半环服务厅"} ]}, {name: "清新分公司", code: "QX", assignee: "", children:[]}, {name: "英德分公司", code: "YD", assignee: "", children:[]}, {name: "佛冈分公司", code: "FG", assignee: "", children:[]} ] }; /* 单击数据行后触发该事件 id:行的id index:行的索引。 data:json格式的行数据对象。 */ function itemClickEvent(id, index, data){ window.location.href="ads"; } /* 通过指定的方法来自定义栏数据 */ function customCheckBox(row, col){ return ""; } function customOrgName(row, col){ var name = row[col.dataField] || ""; return name; } function customLook(row, col){ return "查看"; } //创建一个组件对象 var treeGrid = new TreeGrid(config); treeGrid.show(); /* 展开、关闭所有节点。 isOpen=Y表示展开,isOpen=N表示关闭 */ function expandAll(isOpen){ treeGrid.expandAll(isOpen); } /* 取得当前选中的行,方法返回TreeGridItem对象 */ function selectedItem(){ var treeGridItem = treeGrid.getSelectedItem(); if(treeGridItem!=null){ //获取数据行属性值 //alert(treeGridItem.id + ", " + treeGridItem.index + ", " + treeGridItem.data.name); //获取父数据行 var parent = treeGridItem.getParent(); if(parent!=null){ //jQuery("#currentRow").val(parent.data.name); } //获取子数据行集 var children = treeGridItem.getChildren(); if(children!=null && children.length>0){ jQuery("#currentRow").val(children[0].data.name); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值