diff --git a/components/tree/demo/basic.ts b/components/tree/demo/basic.ts index e9be2a7c740..754d6ca007a 100644 --- a/components/tree/demo/basic.ts +++ b/components/tree/demo/basic.ts @@ -13,6 +13,7 @@ import { NzFormatEmitEvent, NzTreeNode, NzTreeNodeOptions } from 'ng-zorro-antd' [nzExpandedKeys]="defaultExpandedKeys" [nzSelectedKeys]="defaultSelectedKeys" (nzClick)="nzClick($event)" + (nzSelectedKeysChange)="nzSelect($event)" (nzCheckBoxChange)="nzCheck($event)"> ` @@ -45,17 +46,21 @@ export class NzDemoTreeBasicComponent implements OnInit { } ]; nzClick(event: NzFormatEmitEvent): void { - console.log(event, event.selectedKeys, event.keys, event.nodes); + console.log(event, event.selectedKeys, event.keys, event.nodes, this.treeCom.getSelectedNodeList()); } nzCheck(event: NzFormatEmitEvent): void { console.log(event, event.checkedKeys, event.keys, event.nodes); } + // nzSelectedKeys change + nzSelect(keys: string[]): void { + console.log(keys, this.treeCom.getSelectedNodeList()); + } + ngOnInit(): void { setTimeout(() => { - console.log(this.treeCom.getTreeNodes(), this.treeCom.getCheckedNodeList()); + console.log(this.treeCom.getTreeNodes(), this.treeCom.getCheckedNodeList(), this.treeCom.getSelectedNodeList()); }, 500); - } } diff --git a/components/tree/nz-tree-node.component.html b/components/tree/nz-tree-node.component.html index 29bff3ca43f..ca4ecf77fd8 100644 --- a/components/tree/nz-tree-node.component.html +++ b/components/tree/nz-tree-node.component.html @@ -1,6 +1,7 @@
  • - - - - + + + + + + + + @@ -48,7 +53,7 @@ [ngClass]="nzNodeContentLoadingClass"> - + @@ -85,6 +90,7 @@ [nzExpandAll]="nzExpandAll" [nzDefaultExpandAll]="nzDefaultExpandAll" [nzSearchValue]="nzSearchValue" + [nzHideUnMatched]="nzHideUnMatched" [nzBeforeDrop]="nzBeforeDrop" [nzCheckStrictly]="nzCheckStrictly" [nzTreeTemplate]="nzTreeTemplate" diff --git a/components/tree/nz-tree-node.component.ts b/components/tree/nz-tree-node.component.ts index 4796e1ca854..fc22606ff07 100644 --- a/components/tree/nz-tree-node.component.ts +++ b/components/tree/nz-tree-node.component.ts @@ -1,13 +1,22 @@ import { animate, state, style, transition, trigger } from '@angular/animations'; import { - Component, ElementRef, EventEmitter, HostListener, - Input, NgZone, + Component, + ElementRef, + EventEmitter, + HostListener, + Input, + NgZone, OnChanges, - OnInit, Output, Renderer2, - SimpleChanges, - TemplateRef, ViewChild + OnDestroy, + OnInit, + Output, + Renderer2, + SimpleChange, + TemplateRef, + ViewChild } from '@angular/core'; -import { fromEvent, Observable } from 'rxjs'; +import { fromEvent, Observable, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { InputBoolean } from '../core/util/convert'; import { NzFormatBeforeDropEvent, NzFormatEmitEvent } from '../tree/interface'; import { NzTreeNode } from './nz-tree-node'; @@ -35,16 +44,16 @@ import { NzTreeService } from './nz-tree.service'; ] }) -export class NzTreeNodeComponent implements OnInit, OnChanges { +export class NzTreeNodeComponent implements OnInit, OnChanges, OnDestroy { @ViewChild('dragElement') dragElement: ElementRef; @Input() @InputBoolean() nzShowLine: boolean; @Input() @InputBoolean() nzShowExpand: boolean; - @Input() @InputBoolean() nzDraggable: boolean; @Input() @InputBoolean() nzMultiple: boolean; @Input() @InputBoolean() nzCheckable: boolean; @Input() @InputBoolean() nzAsyncData: boolean; @Input() @InputBoolean() nzCheckStrictly: boolean; + @Input() @InputBoolean() nzHideUnMatched = false; @Input() nzTreeTemplate: TemplateRef; @Input() nzBeforeDrop: (confirm: NzFormatBeforeDropEvent) => Observable; @@ -68,6 +77,16 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { return this._nzTreeNode; } + @Input() + set nzDraggable(value: boolean) { + this._nzDraggable = value; + this.handDragEvent(); + } + + get nzDraggable(): boolean { + return this._nzDraggable; + } + /** * @deprecated use * nzExpandAll instead @@ -103,14 +122,10 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { set nzSearchValue(value: string) { this.highlightKeys = []; if (value && this.nzTreeNode.title.includes(value)) { - this.nzTreeNode.isMatched = true; // match the search value const index = this.nzTreeNode.title.indexOf(value); this.highlightKeys.push(this.nzTreeNode.title.slice(0, index)); this.highlightKeys.push(this.nzTreeNode.title.slice(index + value.length, this.nzTreeNode.title.length)); - } else { - // close the node if title does't contain search value - this.nzTreeNode.isMatched = false; } this._searchValue = value; } @@ -145,6 +160,7 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { /** * drag var */ + destory$ = new Subject(); dragPos = 2; dragPosClass: object = { '0' : 'drag-over', @@ -158,11 +174,28 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { _nzTreeNode: NzTreeNode; _searchValue = ''; _nzExpandAll = false; + _nzDraggable = false; + oldAPIIcon = true; + + get nzIcon(): string { + if (this.nzTreeNode && this.nzTreeNode.origin.icon) { + this.oldAPIIcon = this.nzTreeNode.origin.icon.indexOf('anticon') > -1; + } + return this.nzTreeNode && this.nzTreeNode.origin.icon; + } get canDraggable(): boolean | null { return (this.nzDraggable && this.nzTreeNode && !this.nzTreeNode.isDisabled) ? true : null; } + get isShowLineIcon(): boolean { + return !this.nzTreeNode.isLeaf && this.nzShowLine; + } + + get isShowSwitchIcon(): boolean { + return !this.nzTreeNode.isLeaf && !this.nzShowLine; + } + get isSwitcherOpen(): boolean { return (this.nzTreeNode.isExpanded && !this.nzTreeNode.isLeaf); } @@ -171,6 +204,11 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { return (!this.nzTreeNode.isExpanded && !this.nzTreeNode.isLeaf); } + get displayStyle(): string { + // TODO + return (this.nzSearchValue && this.nzHideUnMatched && !this.nzTreeNode.isMatched && !this.nzTreeNode.isExpanded) ? 'none' : ''; + } + /** * reset node class */ @@ -367,22 +405,39 @@ export class NzTreeNodeComponent implements OnInit, OnChanges { }); } - constructor(private nzTreeService: NzTreeService, private ngZone: NgZone, private renderer: Renderer2, private elRef: ElementRef) { - ngZone.runOutsideAngular(() => { - fromEvent(this.elRef.nativeElement, 'dragstart').subscribe((e: DragEvent) => this.handleDragStart(e)); - fromEvent(this.elRef.nativeElement, 'dragenter').subscribe((e: DragEvent) => this.handleDragEnter(e)); - fromEvent(this.elRef.nativeElement, 'dragover').subscribe((e: DragEvent) => this.handleDragOver(e)); - fromEvent(this.elRef.nativeElement, 'dragleave').subscribe((e: DragEvent) => this.handleDragLeave(e)); - fromEvent(this.elRef.nativeElement, 'drop').subscribe((e: DragEvent) => this.handleDragDrop(e)); - fromEvent(this.elRef.nativeElement, 'dragend').subscribe((e: DragEvent) => this.handleDragEnd(e)); + /** + * 监听拖拽事件 + */ + handDragEvent(): void { + this.ngZone.runOutsideAngular(() => { + if (this.nzDraggable) { + this.destory$ = new Subject(); + fromEvent(this.elRef.nativeElement, 'dragstart').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragStart(e)); + fromEvent(this.elRef.nativeElement, 'dragenter').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragEnter(e)); + fromEvent(this.elRef.nativeElement, 'dragover').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragOver(e)); + fromEvent(this.elRef.nativeElement, 'dragleave').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragLeave(e)); + fromEvent(this.elRef.nativeElement, 'drop').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragDrop(e)); + fromEvent(this.elRef.nativeElement, 'dragend').pipe(takeUntil(this.destory$)).subscribe((e: DragEvent) => this.handleDragEnd(e)); + } else { + this.destory$.next(); + this.destory$.complete(); + } }); } + constructor(private nzTreeService: NzTreeService, private ngZone: NgZone, private renderer: Renderer2, private elRef: ElementRef) { + } + ngOnInit(): void { this.setClassMap(); } - ngOnChanges(changes: SimpleChanges): void { + ngOnChanges(changes: { [ propertyName: string ]: SimpleChange }): void { this.setClassMap(); } + + ngOnDestroy(): void { + this.destory$.next(); + this.destory$.complete(); + } } diff --git a/components/tree/nz-tree.component.html b/components/tree/nz-tree.component.html index b16cf9e4ac7..61eb904b3e5 100644 --- a/components/tree/nz-tree.component.html +++ b/components/tree/nz-tree.component.html @@ -12,6 +12,7 @@ [nzAsyncData]="nzAsyncData" [nzMultiple]="nzMultiple" [nzSearchValue]="nzSearchValue" + [nzHideUnMatched]="nzHideUnMatched" [nzBeforeDrop]="nzBeforeDrop" [nzCheckStrictly]="nzCheckStrictly" [nzExpandAll]="nzExpandAll" diff --git a/components/tree/nz-tree.component.ts b/components/tree/nz-tree.component.ts index a53465f4dcd..87b40e79b47 100644 --- a/components/tree/nz-tree.component.ts +++ b/components/tree/nz-tree.component.ts @@ -4,11 +4,15 @@ import { ContentChild, EventEmitter, Input, + OnChanges, OnDestroy, - OnInit, Output, TemplateRef + OnInit, + Output, + SimpleChange, + TemplateRef } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable, Subject, Subscription } from 'rxjs'; +import { Observable, ReplaySubject, Subscription } from 'rxjs'; import { isNotNil } from '../core/util/check'; import { InputBoolean } from '../core/util/convert'; import { NzFormatBeforeDropEvent, NzFormatEmitEvent } from '../tree/interface'; @@ -28,7 +32,7 @@ import { NzTreeService } from './nz-tree.service'; ] }) -export class NzTreeComponent implements OnInit, OnDestroy { +export class NzTreeComponent implements OnInit, OnChanges, OnDestroy { @Input() @InputBoolean() nzShowIcon = false; @Input() @InputBoolean() nzShowLine = false; @Input() @InputBoolean() nzCheckStrictly = false; @@ -38,6 +42,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { @Input() @InputBoolean() nzDraggable = false; @Input() @InputBoolean() nzMultiple = false; @Input() @InputBoolean() nzExpandAll: boolean = false; + @Input() @InputBoolean() nzHideUnMatched = false; /** * @deprecated use * nzExpandAll instead @@ -48,7 +53,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { @Input() // tslint:disable-next-line:no-any set nzData(value: any[]) { - if (Array.isArray(value) && value.length > 0) { + if (Array.isArray(value)) { if (!this.nzTreeService.isArrayOfNzTreeNode(value)) { // has not been new NzTreeNode this.nzNodes = value.map(item => (new NzTreeNode(item))); @@ -59,7 +64,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { this.nzTreeService.initTree(this.nzNodes); } else { if (value !== null) { - console.warn('ngModel only accepts an array and should be not empty'); + console.warn('ngModel only accepts an array and must be not empty'); } } } @@ -70,9 +75,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { */ @Input() set nzDefaultExpandedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzExpandedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzExpandedKeys', keys: value }); } /** @@ -81,9 +84,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { */ @Input() set nzDefaultSelectedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzSelectedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzSelectedKeys', keys: value }); } /** @@ -92,30 +93,22 @@ export class NzTreeComponent implements OnInit, OnDestroy { */ @Input() set nzDefaultCheckedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzCheckedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzCheckedKeys', keys: value }); } @Input() set nzExpandedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzExpandedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzExpandedKeys', keys: value }); } @Input() set nzSelectedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzSelectedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzSelectedKeys', keys: value }); } @Input() set nzCheckedKeys(value: string[]) { - setTimeout(() => { - this.nzDefaultSubject.next({ type: 'nzCheckedKeys', keys: value }); - }); + this.nzDefaultSubject.next({ type: 'nzCheckedKeys', keys: value }); } @Input() @@ -159,9 +152,9 @@ export class NzTreeComponent implements OnInit, OnDestroy { // tslint:disable-next-line:no-any @ContentChild('nzTreeTemplate') nzTreeTemplate: TemplateRef; - _searchValue = ''; + _searchValue = null; // tslint:disable-next-line:no-any - nzDefaultSubject = new Subject(); + nzDefaultSubject = new ReplaySubject(6); nzDefaultSubscription: Subscription; nzNodes: NzTreeNode[] = []; prefixCls = 'ant-tree'; @@ -207,7 +200,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { } writeValue(value: NzTreeNode[]): void { - if (Array.isArray(value) && value.length > 0) { + if (Array.isArray(value)) { this.nzNodes = value; this.nzTreeService.conductOption.isCheckStrictly = this.nzCheckStrictly; this.nzTreeService.initTree(this.nzNodes); @@ -232,7 +225,7 @@ export class NzTreeComponent implements OnInit, OnDestroy { ngOnInit(): void { this.setClassMap(); this.nzDefaultSubscription = this.nzDefaultSubject.subscribe((data: { type: string, keys: string[] }) => { - if (data.keys.length === 0) { + if (!data || !data.keys) { return; } switch (data.type) { @@ -252,6 +245,12 @@ export class NzTreeComponent implements OnInit, OnDestroy { }); } + ngOnChanges(changes: { [ propertyName: string ]: SimpleChange }): void { + if (changes.nzCheckStrictly) { + this.nzTreeService.conductOption.isCheckStrictly = changes.nzCheckStrictly.currentValue; + } + } + ngOnDestroy(): void { if (this.nzDefaultSubscription) { this.nzDefaultSubscription.unsubscribe(); diff --git a/components/tree/nz-tree.service.ts b/components/tree/nz-tree.service.ts index c806bc4ae77..029795ffb3e 100644 --- a/components/tree/nz-tree.service.ts +++ b/components/tree/nz-tree.service.ts @@ -47,32 +47,32 @@ export class NzTreeService { * get some list */ getSelectedNodeList(): NzTreeNode[] { - return this.selectedNodeList; + return this.conductNodeState('select'); } /** * return checked nodes */ getCheckedNodeList(): NzTreeNode[] { - return this.conductCheck('check'); + return this.conductNodeState('check'); } getHalfCheckedNodeList(): NzTreeNode[] { - return this.conductCheck('halfCheck'); + return this.conductNodeState('halfCheck'); } /** * return expanded nodes */ getExpandedNodeList(): NzTreeNode[] { - return this.expandedNodeList; + return this.conductNodeState('expand'); } /** * return search matched nodes */ getMatchedNodeList(): NzTreeNode[] { - return this.matchedNodeList; + return this.conductNodeState('match'); } // tslint:disable-next-line:no-any @@ -99,7 +99,6 @@ export class NzTreeService { }); }; calc(nzNodes); - } /** @@ -216,15 +215,15 @@ export class NzTreeService { } /** - * conduct checked keys + * conduct checked/selected/expanded keys */ - conductCheck(type: string = 'check'): NzTreeNode[] { - const checkedNodeList = []; + conductNodeState(type: string = 'check'): NzTreeNode[] { + const resultNodesList = []; const loop = (node: NzTreeNode) => { switch (type) { case 'check': if (node.isChecked) { - checkedNodeList.push(node); + resultNodesList.push(node); } if (!this.conductOption.isCheckStrictly) { if (!node.isChecked) { @@ -241,19 +240,43 @@ export class NzTreeService { case 'halfCheck': if (!this.conductOption.isCheckStrictly) { if (node.isHalfChecked) { - checkedNodeList.push(node); + resultNodesList.push(node); node.getChildren().forEach(child => { loop(child); }); } } break; + case 'select': + if (node.isSelected) { + resultNodesList.push(node); + } + node.getChildren().forEach(child => { + loop(child); + }); + break; + case 'expand': + if (node.isExpanded) { + resultNodesList.push(node); + } + node.getChildren().forEach(child => { + loop(child); + }); + break; + case 'match': + if (node.isMatched) { + resultNodesList.push(node); + } + node.getChildren().forEach(child => { + loop(child); + }); + break; } }; this.rootNodes.forEach(node => { loop(node); }); - return checkedNodeList; + return resultNodesList; } /** @@ -350,10 +373,12 @@ export class NzTreeService { const searchChild = (n: NzTreeNode) => { if (value && n.title.includes(value)) { // match the node + n.isMatched = true; this.matchedNodeList.push(n); // expand parentNode expandParent(n); } else { + n.isMatched = false; n.setExpanded(false); this.setExpandedNodeList(n); } @@ -475,19 +500,16 @@ export class NzTreeService { break; case 'click': case 'dblclick': - // TODO: Deprecated Object.assign(emitStructure, { 'selectedKeys': this.getSelectedNodeList() }); Object.assign(emitStructure, { 'nodes': this.getSelectedNodeList() }); Object.assign(emitStructure, { 'keys': this.getSelectedNodeList().map(n => n.key) }); break; case 'check': - // TODO: Deprecated Object.assign(emitStructure, { 'checkedKeys': this.getCheckedNodeList() }); Object.assign(emitStructure, { 'nodes': this.getCheckedNodeList() }); Object.assign(emitStructure, { 'keys': this.getCheckedNodeList().map(n => n.key) }); break; case 'search': - // TODO: Deprecated Object.assign(emitStructure, { 'matchedKeys': this.getMatchedNodeList() }); Object.assign(emitStructure, { 'nodes': this.getMatchedNodeList() }); Object.assign(emitStructure, { 'keys': this.getMatchedNodeList().map(n => n.key) }); diff --git a/components/tree/nz-tree.spec.ts b/components/tree/nz-tree.spec.ts index dbb5deea30e..a9f1982ebfc 100644 --- a/components/tree/nz-tree.spec.ts +++ b/components/tree/nz-tree.spec.ts @@ -1,5 +1,5 @@ import { Component, ViewChild } from '@angular/core'; -import { async, fakeAsync, tick, TestBed } from '@angular/core/testing'; +import { async, fakeAsync, flush, tick, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -66,6 +66,8 @@ describe('nz-tree', () => { }); it('test new NzTreeNode of nzData', fakeAsync(() => { + fixture.detectChanges(); + flush(); fixture.detectChanges(); treeInstance.nodes = [ { title : '0-0', @@ -523,8 +525,6 @@ describe('nz-tree', () => { }); it('should get correctly nodes', fakeAsync(() => { - fixture.detectChanges(); - tick(200); fixture.detectChanges(); // unsupported type, will console `ngModel only accepts an array and should be not empty` expect(treeInstance.treeComponent.getCheckedNodeList().length).toEqual(1); @@ -535,8 +535,8 @@ describe('nz-tree', () => { expect(treeInstance.treeComponent.getHalfCheckedNodeList()[ 0 ].key).toEqual('1001'); expect(treeInstance.treeComponent.getSelectedNodeList().length).toEqual(2); // test clear children - treeInstance.treeComponent.getTreeNodes()[0].clearChildren(); - expect(treeInstance.treeComponent.getTreeNodes()[0].getChildren().length).toEqual(0); + treeInstance.treeComponent.getTreeNodes()[ 0 ].clearChildren(); + expect(treeInstance.treeComponent.getTreeNodes()[ 0 ].getChildren().length).toEqual(0); }));