Angular使用ngx-treeview实现异步加载多选选择树

最近遇到个问题,ngx-treeview这个控件不支持原生的异步数据加载,也没有点击展开时的“onExpandCollapse”事件,导致只能一次性地把全部数据加载进去,很慢。我并非前端开发人员,对typescript、angular这些都不太熟悉,所以花了两天时间才解决,以此记录。

ngx-treeview官方文档:ngx-treeview - npm (npmjs.com)

以及小例子:ngx-treeview (leovo2708.github.io)

我的方法:

自定义ng-template,所有的事件都自己写。

它大致长个样子:

每一级的下级都是在点击“展开”后才加载的。以下为实现细节: 

component.html:

<div class="form-group">
    <ngx-treeview [config]="config"
                  [items]="itemsManager"
                  [itemTemplate]="itemTemplate"
                  (selectedChange)="selectedValues = $event">
    </ngx-treeview>
</div>

<ng-template #itemTemplate let-item="item" let-onCollapseExpand="onCollapseExpand" let-onCheckedChange="onCheckedChange">
    <div class="form-inline row-item" style="display: flex;align-items: center;flex-flow: row wrap;">
        <i style="margin-top: -.3rem;" *ngIf="item.value>=0" (click)="orgUnfold(item)" aria-hidden="true" class="fa" [class.fa-caret-right]="item.collapsed"
           [class.fa-caret-down]="!item.collapsed"></i>
        <i style="margin-top: -.3rem;width: 13px;" *ngIf="item.value>=0" aria-hidden="true">&nbsp;</i>
        <div class="form-check">
            <input type="checkbox" class="form-check-input" *ngIf="item.value>=0" (ngModelChange)="choseItem(item)" [disabled]="item.disabled" [(ngModel)]="item.checked">
            <!--[indeterminate]="item.indeterminate" />-->
            <!--<i aria-hidden="true" [class]="item.value['icon']"></i>-->
            <label class="form-check-label" (click)="choseItem(item);item.checked=!item.checked">
                {{item.text}}
            </label>
        </div>
    </div>
</ng-template>

component.ts:

import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {OrgService} from '../../../_services';
import {ToastrService} from 'ngx-toastr';
import {DownlineTreeviewItem, TreeviewConfig, TreeviewItem} from 'ngx-treeview';

declare var $:any;

@Component({
    selector: 'app-selectTree-custs',
    templateUrl: './select-custs-treeview.component.html',
    styleUrls: ['./select-custs-treeview.component.css']
})
export class SelectCustsTreeComponent implements OnInit {
    config: any;
    selectedValues: any;
    itemsManager: TreeviewItem[];

    <!-- 从父控件输入itemsManagerPrev -->
    @Input() itemsManagerPrev: TreeviewItem[];
    <!-- 输出给父控件的事件onSend,每次checkbox变化的时候将变化值发给父控件 -->
    @Output() onSend: EventEmitter<number[]> = new EventEmitter<number[]>();
    <!-- 输出给父控件的事件onSendTree,每次checkbox变化的时候将树选择器的整体数据同步给父控件 -->
    @Output() onSendTree: EventEmitter<TreeviewItem[]> = new EventEmitter<TreeviewItem[]>();

    constructor(private orgService:OrgService,
                    private toastr:ToastrService) {
    }

    ngOnInit() {
        this.config = TreeviewConfig.create({
                hasAllCheckBox: false,
                hasFilter: false,
                hasCollapseExpand: false,
                decoupleChildFromParent: true,
                maxHeight: 400
        });
        if (!this.itemsManagerPrev) {
            this.getOrgRoot();
        } else {
            this.itemsManager = this.itemsManagerPrev;
        }
    }

    <!-- 初始化第一层数据 -->
    getOrgRoot() {
        this.orgService.getChildOrg(0).subscribe( <!-- 这个是调用自己服务的接口 -->
            result => {
                let trItems = [];
                result.forEach(orgInfo => {
                    const trItem = new TreeviewItem({
                        text: orgInfo.label,
                        value: orgInfo.value,
                        children: [new TreeviewItem({text: "查询中...", disabled: true, value: -10000, checked: false})],
                        collapsed: true,
                        checked: false,
                        disabled: false
                    });
                    trItems.push(trItem);
                });
                this.itemsManager = trItems;
            },
            err => {
                this.toastr.error(err.error.msg);
            }
        )
    }

    <!-- 某一行点击展开时,触发该函数 -->
    <!-- 定义了一些特殊值,-10000代表这个节点的下一级节点数据已经有了,-20000代表这个节点没有下一级了 -->
    orgUnfold(item: TreeviewItem) {
        if (!item.collapsed) {
            item.collapsed = true;
            return;
        } else if (item.children[0].value != -10000) { 
            item.collapsed = false;
            return;
        }
        item.collapsed = false;
        this.orgService.getChildOrg(item.value).subscribe(
            result => {
                let trItems = [];
                var itemChecked = item.checked;
                result.forEach(orgInfo => {
                    const trItem = new TreeviewItem({
                        text: orgInfo.label,
                        value: orgInfo.value,
                        children: [new TreeviewItem({text: "查询中...", disabled: true, value: -10000, checked: false})],
                        collapsed: true,
                        checked: false,
                        disabled: false
                    });
                    trItems.push(trItem);
                });
                if (trItems.length > 0) {
                    item.children = trItems;
                    item.checked = itemChecked;
                } else {
                    item.children = [new TreeviewItem({text: "无结果", disabled: true, value: -20000, checked: false})];
                    item.checked = itemChecked;
                }
                this.onSendTree.emit(this.itemsManager);
            },
            err => {
                var itemChecked = item.checked;
                this.toastr.error(err.error.msg);
                item.collapsed = true;
                item.children = [new TreeviewItem({text: "查询中...", disabled: true, value: -10000, checked: false})];
                item.checked = itemChecked;
            }
        )
    }

    <!-- 每当节点被点击,checkbox变化的时候触发 -->
    choseItem(item) {
        console.log("selectedValues="+this.selectedValues);
        if (item.value<0) {
            return;
        }
        console.log("selectedOrgIds:"+item.checked);
        if (!item.checked) {
            this.onSend.emit([1,item.value]);  <!-- 向父控件同步数据 -->
        } else {
            this.onSend.emit([-1,item.value]); <!-- 向父控件同步数据 -->
        }
        this.onSendTree.emit(this.itemsManager); <!-- 向父控件同步数据 -->
    }

}

父控件调用这个选择树:

(这里的父控件是一个按钮弹窗,以下是按钮以及弹窗的代码)

<!-- 非必要代码省略 -->

<button class="btn btn-sm btn-success" (click)="selectOrg(OrgFilter)">{{orgButtonName | limitLength:6}}</button>

<!-- 非必要代码省略 -->

<ng-template #OrgFilter>
    <div class="modal-header">
        <h4 class="modal-title pull-left">选择组织</h4>
        <button type="button" class="close pull-right" aria-label="Close" (click)="orgModal.hide()">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>
    <div class="modal-body">
        <app-selectTree-custs (onSend) = "selectedOrgChanged($event)" (onSendTree) = "refreshTree($event)" [itemsManagerPrev] = "orgTree">
        </app-selectTree-custs>
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default pull-left" (click)="orgReset()">重置</button>
        <button type="button" class="btn btn-primary pull-right" (click)="orgFilter()">确定</button>
    </div>
</ng-template>
selectedOrgIds: number[];
orgTree: any;
orgButtonName: string = '选择客户';
...

constructor(
        this.selectedOrgIds = [];
        this.orgTree = null;
        ...
)

...

selectOrg(template:TemplateRef<any>){
        this.orgModal = this.modalService.show(template,{ class: 'modal-sm' });
}

<!-- 同步treeview子控件中复选框选择的数据 -->
selectedOrgChanged(content: number[]) {
        if (content[0] == -1) {
        var index = 0;
        while (index != -1) {
            index = this.selectedOrgIds.indexOf(content[1]);
            if(index != -1) {
                this.selectedOrgIds.splice(index,1);
            }
        }
        } else {
            var index = this.selectedOrgIds.indexOf(content[1]);
            if (index == -1) {
                this.selectedOrgIds.push(content[1]);
            }
        }
        console.log("this.selectedOrgIds:",this.selectedOrgIds);
}

<!-- 从treeview子控件同步它的全部数据 -->
refreshTree(orgTree: any) {
        this.orgTree = orgTree;
}

<!-- 确定按钮 -->
orgFilter(){
        var selOrgs = this.selectedOrgIds.toString();
        this.teamid = this.activatedRoute.snapshot.paramMap.get('teamid');
        if (this.teamid != null || this.teamid != undefined) {
            this.getTeamUsers(this.teamid, this.page,this.keyword); <!-- 调服务接口的函数 -->
        } else {
            this.getUsers(this.page,this.keyword,selOrgs); <!-- 调服务接口的函数 -->
        }
        this.orgModal.hide();
        if (this.selectedOrgIds.length > 0) {
            this.orgButtonName = '已选' + this.selectedOrgIds.length + '个组织';
        } else {
            this.orgButtonName = '选择组织';
        }
}

<!-- 重置按钮 -->
orgReset(){
        var setCheckedFalse = function(item) {
            item.checked = false;
            if (item.children == null) {
                return;
            }
            item.children.forEach(ch => {
                ch.checked = false;
                setCheckedFalse(ch);
            });
        }
        this.orgTree.forEach(item => {
            setCheckedFalse(item);
        });
        this.selectedOrgIds = [];
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值