Skip to content

Commit a83c1e4

Browse files
robertjanetzkoGeorgii Rychko
authored andcommitted
feat(*): add ability to pass a template to the tree for nodes rendering
* templates can be used for rendering nodes * removed diagnostics * template example for demo app * tests for tree templates
1 parent 6543783 commit a83c1e4

File tree

5 files changed

+135
-6
lines changed

5 files changed

+135
-6
lines changed

src/demo/app/app.component.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ declare const alertify: any;
7878
</button>
7979
</div>
8080
</div>
81+
<div class="tree-container">
82+
<div class="tree-info"><p class="tree-title">Programming languages tree</p>
83+
<p class="notice">this tree is using a custom template</p></div>
84+
<div class="tree-content">
85+
<tree [tree]="icons"
86+
[settings]="settings"
87+
(nodeRemoved)="onNodeRemoved($event)"
88+
(nodeRenamed)="onNodeRenamed($event)"
89+
(nodeSelected)="onNodeSelected($event)"
90+
(nodeMoved)="onNodeMoved($event)"
91+
(nodeCreated)="onNodeCreated($event)">
92+
<ng-template let-node>
93+
<i class="fa {{node.icon}}"></i> <span class="node-name" [innerHTML]="node.value"></span>
94+
</ng-template>
95+
</tree>
96+
</div>
97+
</div>
8198
</div>
8299
`,
83100
styles: [`
@@ -439,6 +456,39 @@ export class AppComponent implements OnInit {
439456

440457
@ViewChild('treeFFS') public treeFFS;
441458

459+
public icons: TreeModel = {
460+
value: 'Icons',
461+
children: [
462+
{
463+
value: 'Web Application Icons',
464+
children: [
465+
{value: 'calendar', icon: 'fa-calendar' },
466+
{value: 'download', icon: 'fa-download' },
467+
{value: 'group', icon: 'fa-group' },
468+
{value: 'print', icon: 'fa-print' }
469+
]
470+
},
471+
{
472+
value: 'Hand Icons',
473+
children: [
474+
{value: 'pointer', icon: 'fa-hand-pointer-o' },
475+
{value: 'grab', icon: 'fa-hand-rock-o' },
476+
{value: 'thumbs up', icon: 'fa-thumbs-o-up ' },
477+
{value: 'thumbs down', icon: 'fa-thumbs-o-down' }
478+
]
479+
},
480+
{
481+
value: 'File Type Icons',
482+
children: [
483+
{value: 'file', icon: 'fa-file-o' },
484+
{value: 'audio', icon: 'fa-file-audio-o' },
485+
{value: 'movie', icon: 'fa-file-movie-o ' },
486+
{value: 'archive', icon: 'fa-file-zip-o' }
487+
]
488+
},
489+
]
490+
};
491+
442492
private static logEvent(e: NodeEvent, message: string): void {
443493
console.log(e);
444494
alertify.message(`${message}: ${e.node.value}`);

src/tree-internal.component.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ElementRef, Inject, Input, OnDestroy, OnInit } from '@angular/core';
1+
import { Component, ElementRef, TemplateRef, Inject, Input, OnDestroy, OnInit } from '@angular/core';
22
import * as TreeTypes from './tree.types';
33
import { Tree } from './tree';
44
import { TreeController } from './tree-controller';
@@ -29,8 +29,9 @@ import { get } from './utils/fn.utils';
2929
[class.node-selected]="isSelected"
3030
(click)="onNodeSelected($event)">
3131
<div *ngIf="tree.nodeTemplate" class="node-template" [innerHTML]="tree.nodeTemplate | safeHtml"></div>
32-
<span class="node-name" [innerHTML]="tree.value | safeHtml"></span>
32+
<span *ngIf="!template" class="node-name" [innerHTML]="tree.value | safeHtml"></span>
3333
<span class="loading-children" *ngIf="tree.childrenAreBeingLoaded()"></span>
34+
<ng-template [ngTemplateOutlet]="template" [ngOutletContext]="{ $implicit: tree.node }"></ng-template>
3435
</div>
3536
3637
<input type="text" class="node-value"
@@ -48,7 +49,7 @@ import { get } from './utils/fn.utils';
4849
<node-menu *ngIf="isRightMenuVisible" (menuItemSelected)="onMenuItemSelected($event)"></node-menu>
4950
5051
<ng-template [ngIf]="tree.isNodeExpanded()">
51-
<tree-internal *ngFor="let child of tree.childrenAsync | async" [tree]="child"></tree-internal>
52+
<tree-internal *ngFor="let child of tree.childrenAsync | async" [tree]="child" [template]="template"></tree-internal>
5253
</ng-template>
5354
</li>
5455
</ul>
@@ -61,6 +62,9 @@ export class TreeInternalComponent implements OnInit, OnDestroy {
6162
@Input()
6263
public settings: TreeTypes.Ng2TreeSettings;
6364

65+
@Input()
66+
public template: TemplateRef<any>;
67+
6468
public isSelected = false;
6569
public isRightMenuVisible = false;
6670
public isLeftMenuVisible = false;

src/tree.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
Input, Component, OnInit, EventEmitter, Output, Inject, OnChanges, SimpleChanges, ViewChild,
3-
OnDestroy
3+
OnDestroy, TemplateRef, ContentChild
44
} from '@angular/core';
55
import { TreeService } from './tree.service';
66
import * as TreeTypes from './tree.types';
@@ -11,7 +11,7 @@ import { Subscription } from 'rxjs/Subscription';
1111

1212
@Component({
1313
selector: 'tree',
14-
template: `<tree-internal #rootComponent [tree]="tree" [settings]="settings"></tree-internal>`,
14+
template: `<tree-internal #rootComponent [tree]="tree" [settings]="settings" [template]="template"></tree-internal>`,
1515
providers: [TreeService]
1616
})
1717
export class TreeComponent implements OnInit, OnChanges, OnDestroy {
@@ -52,6 +52,8 @@ public loadNextLevel: EventEmitter<any> = new EventEmitter();
5252
public tree: Tree;
5353
@ViewChild('rootComponent') public rootComponent;
5454

55+
@ContentChild(TemplateRef) public template;
56+
5557
private subscriptions: Subscription[] = [];
5658

5759
public constructor(@Inject(TreeService) private treeService: TreeService) {

src/tree.types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export interface TreeModel {
2525
emitLoadNextLevel?: boolean;
2626
_status?: TreeStatus;
2727
_foldingType?: FoldingType;
28-
}
28+
[additionalData: string]: any;
29+
}
2930

3031
export interface CssClasses {
3132
/* The class or classes that should be added to the expanded node */
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import { By } from '@angular/platform-browser';
3+
import { Component, ElementRef, DebugElement } from '@angular/core';
4+
import { TreeInternalComponent } from '../src/tree-internal.component';
5+
import { TreeComponent } from '../src/tree.component';
6+
import { TreeModel, Ng2TreeSettings } from '../src/tree.types';
7+
import { TreeService } from '../src/tree.service';
8+
import { NodeMenuService } from '../src/menu/node-menu.service';
9+
import { NodeMenuComponent } from '../src/menu/node-menu.component';
10+
import { NodeDraggableService } from '../src/draggable/node-draggable.service';
11+
import { NodeDraggableDirective } from '../src/draggable/node-draggable.directive';
12+
import { NodeEditableDirective } from '../src/editable/node-editable.directive';
13+
import { NodeMenuAction } from '../src/menu/menu.events';
14+
import * as EventUtils from '../src/utils/event.utils';
15+
import { CapturedNode } from '../src/draggable/captured-node';
16+
import { SafeHtmlPipe } from '../src/utils/safe-html.pipe';
17+
18+
let fixture;
19+
let masterInternalTreeEl;
20+
let masterComponentInstance;
21+
22+
const tree: TreeModel = {
23+
value: 'Master', icon: 'icon0',
24+
children: [
25+
{value: 'Servant#1', icon: 'icon1'},
26+
{value: 'Servant#2', icon: 'icon2'}
27+
]
28+
};
29+
30+
@Component({
31+
template: `<div><tree id="master" [tree]="tree"><ng-template let-node><span class="icon">{{node.icon}}</span><span class="value">{{node.value}}</span></ng-template></tree></div>`
32+
})
33+
class TestComponent {
34+
public tree: TreeModel = tree;
35+
public constructor(public treeHolder: ElementRef) {}
36+
}
37+
38+
describe('template for tree', () => {
39+
beforeEach(() => {
40+
TestBed.configureTestingModule({
41+
declarations: [TestComponent, TreeInternalComponent, TreeComponent, NodeEditableDirective, NodeMenuComponent, NodeDraggableDirective, SafeHtmlPipe],
42+
providers: [NodeMenuService, NodeDraggableService, TreeService]
43+
});
44+
45+
fixture = TestBed.createComponent(TestComponent);
46+
47+
masterInternalTreeEl = fixture.debugElement.query(By.css('#master')).query(By.directive(TreeInternalComponent));
48+
masterComponentInstance = masterInternalTreeEl.componentInstance;
49+
50+
fixture.detectChanges();
51+
});
52+
53+
it('should not render default node', () => {
54+
const foldingEl: DebugElement[] = masterInternalTreeEl.queryAll(By.css('.node-name'));
55+
expect(foldingEl.length).toEqual(0);
56+
});
57+
58+
it('should render the template', () => {
59+
const icons: DebugElement[] = masterInternalTreeEl.queryAll(By.css('.icon'));
60+
expect(icons.length).toEqual(3);
61+
expect(icons[0].nativeElement.innerHTML).toEqual('icon0');
62+
expect(icons[1].nativeElement.innerHTML).toEqual('icon1');
63+
expect(icons[2].nativeElement.innerHTML).toEqual('icon2');
64+
65+
const values: DebugElement[] = masterInternalTreeEl.queryAll(By.css('.value'));
66+
expect(values.length).toEqual(3);
67+
expect(values[0].nativeElement.innerHTML).toEqual('Master');
68+
expect(values[1].nativeElement.innerHTML).toEqual('Servant#1');
69+
expect(values[2].nativeElement.innerHTML).toEqual('Servant#2');
70+
});
71+
72+
});

0 commit comments

Comments
 (0)