import {TreeModule,TreeNode} from 'primeng/primeng';
Tree component requires an array of TreeNode objects as its value. Let's begin with the TreeNode api.
Name | Type | Default | Description |
---|---|---|---|
label | string | null | Label of the node. |
data | any | null | Data represented by the node. |
icon | string | null | Icon of the node to display next to content. |
expandedIcon | string | null | Icon to use in expanded state. |
collapsedIcon | string | null | Icon to use in collapsed state. |
children | TreeNode[] | null | An array of treenodes as children. |
leaf | boolean | null | Specifies if the node has children. Used in lazy loading. |
style | string | null | Inline style of the node. |
styleClass | string | null | Style class of the node. |
expanded | boolean | null | Whether the node is in an expanded or collapsed state. |
type | string | null | Type of the node to match template type. |
orientation | string | vertical | Defines the orientation of the tree, valid values are 'vertical' and 'horizontal'. |
Most of the time, nodes will be loaded from a remote datasoure, here is an example NodeService that fetches the data from a json file.
@Injectable()
export class NodeService {
constructor(private http: Http) {}
getFiles() {
return this.http.get('showcase/resources/data/files.json')
.toPromise()
.then(res => <TreeNode[]> res.json().data)
.then(data => { return data; });
}
}
The files.json file consists of sample data. In a real application, this should be a dynamic response generated from the remote call.
{
"data":
[
{
"label": "Documents",
"data": "Documents Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"children": [{
"label": "Work",
"data": "Work Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"children": [{"label": "Expenses.doc", "icon": "fa-file-word-o", "data": "Expenses Document"}, {"label": "Resume.doc", "icon": "fa-file-word-o", "data": "Resume Document"}]
},
{
"label": "Home",
"data": "Home Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"children": [{"label": "Invoices.txt", "icon": "fa-file-word-o", "data": "Invoices for this month"}]
}]
},
{
"label": "Pictures",
"data": "Pictures Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"children": [
{"label": "barcelona.jpg", "icon": "fa-file-image-o", "data": "Barcelona Photo"},
{"label": "logo.jpg", "icon": "fa-file-image-o", "data": "PrimeFaces Logo"},
{"label": "primeui.png", "icon": "fa-file-image-o", "data": "PrimeUI Logo"}]
},
{
"label": "Movies",
"data": "Movies Folder",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"children": [{
"label": "Al Pacino",
"data": "Pacino Movies",
"children": [{"label": "Scarface", "icon": "fa-file-video-o", "data": "Scarface Movie"}, {"label": "Serpico", "icon": "fa-file-video-o", "data": "Serpico Movie"}]
},
{
"label": "Robert De Niro",
"data": "De Niro Movies",
"children": [{"label": "Goodfellas", "icon": "fa-file-video-o", "data": "Goodfellas Movie"}, {"label": "Untouchables", "icon": "fa-file-video-o", "data": "Untouchables Movie"}]
}]
}
]
}
The component that uses this service makes a call to getFiles() and assigns them back to files property that is bound to the tree.
export class TreeDemoComponent implements OnInit {
files: TreeNode[];
constructor(private nodeService: NodeService) {}
ngOnInit() {
this.nodeService.getFiles().then(files => this.files = files);
}
}
<p-tree [value]="files"></p-tree>
Tree supports two selection methods, single and multiple. Selection is enabled by setting selectionMode property and providing a single TreeNode or an array of TreeNodes to reference the selections depending on the selection mode.
export class TreeDemoComponent implements OnInit {
files: TreeNode[];
selectedFile: TreeNode;
constructor(private nodeService: NodeService) {}
ngOnInit() {
this.nodeService.getFiles().then(files => this.files = files);
}
}
<p-tree [value]="files" selectionMode="single" [(selection)]="selectedFile"></p-tree>
In multiple mode, selection property should be an array.
export class TreeDemoComponent implements OnInit {
files: TreeNode[];
selectedFiles: TreeNode[];
constructor(private nodeService: NodeService) {}
ngOnInit() {
this.nodeService.getFiles().then(files => this.files = files);
}
}
<p-tree [value]="files" selectionMode="single" [(selection)]="selectedFiles"></p-tree>
Tree provides onNodeSelect and onNodeUnselect options as callbacks for selection feature.
<p-tree [value]="files" selectionMode="single" [(selection)]="selectedFiles" (onNodeSelect)="nodeSelect($event)"></p-tree>
export class TreeDemoComponent implements OnInit {
files: TreeNode[];
selectedFiles: TreeNode[];
constructor(private nodeService: NodeService) {}
ngOnInit() {
this.nodeService.getFiles().then(files => this.files = files);
}
nodeSelect(event) {
//event.node = selected node
}
}
Icon of a treenode is defined using the icon property, if you need an icon depending on the expand or collapse state, use expandedIcon and collapsedIcon instead.
By default label of a treenode is displayed inside a tree node, in case you need to place custom content define a pTemplate that gets the treenode as an implicit variable. Example below places an input field to create editable treenodes.
<h3>Template</h3>
<p-tree [value]="files">
<template let-node pTemplate type="default">
<input [(ngModel)]="node.label" type="text" style="width:100%">
</template>
</p-tree>
Multiple templates are supported by matching the type property of the TreeNode with the type of pTemplate. If a node as no type, then default template is used.
<h3>Template</h3>
<p-tree [value]="files">
<template let-node pTemplate type="picture">
<img [attrs.src]="picture.path">
</template>
<template let-node pTemplate type="default">
<input [(ngModel)]="node.label" type="text" style="width:100%">
</template>
</p-tree>
Tree has exclusive integration with context menu created by binding a menu instance to the tree.
<p-tree [value]="files" selectionMode="single" [(selection)]="selectedFile2" [contextMenu]="cm"></p-tree>
<p-contextMenu #cm [model]="items"></p-contextMenu>
Lazy loading is handy to deal with large datasets. Instead of loading the whole tree, nodes can be loaded at onNodeExpand event. Important part of implementing lazy loading is defining leaf property of a node as false, this will instruct tree to display an arrow icon to indicate there are children of this node although they are not loaded yet. When the lazy node is expanded, onNodeExpand is called and a remote call can be made to add the children to the expanded node.
<p-tree [value]="files" (onNodeExpand)="loadNode($event)"></p-tree>
export class TreeDemoComponent implements OnInit {
files: TreeNode[];
selectedFiles: TreeNode[];
constructor(private nodeService: NodeService) {}
ngOnInit() {
//initial nodes
this.nodeService.getFiles().then(files => this.files = files);
}
loadNode(event) {
if(event.node) {
//in a real application, make a call to a remote url to load children of the current node and add the new nodes as children
this.nodeService.getLazyFiles().then(nodes => event.node.children = nodes);
}
}
}
Assume at ngOnInit tree is initialized with a data like below that has nodes having no actual children but leaf property is set false.
{
"data":
[
{
"label": "Lazy Node 0",
"data": "Node 0",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"leaf": false
},
{
"label": "Lazy Node 1",
"data": "Node 1",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"leaf": false
},
{
"label": "Lazy Node 1",
"data": "Node 2",
"expandedIcon": "fa-folder-open",
"collapsedIcon": "fa-folder",
"leaf": false
}
]
}
Horizontal mode is the alternative option for orientation.
<p-tree [value]="files" orientation="horizontal"></p-tree>
Name | Type | Default | Description |
---|---|---|---|
value | array | null | An array of treenodes. |
selectionMode | string | null | Defines the selection mode, valid values "single" and "multiple". |
selection | any | null | A single treenode instance or an array to refer to the selections. |
style | string | null | Inline style of the component. |
styleClass | string | null | Style class of the component. |
contextMenu | ContextMenu | null | Context menu instance. |
Name | Parameters | Description |
---|---|---|
onNodeSelect | event.originalEvent: browser event event.node: Selected node instance. |
Callback to invoke when a node is selected. |
onNodeUnselect | event.originalEvent: browser event event.node: Unselected node instance. |
Callback to invoke when a node is unselected. |
onNodeExpand | event.originalEvent: browser event event.node: Expanded node instance. |
Callback to invoke when a node is expanded. |
onNodeCollapse | event.originalEvent: browser event event.node: Collapsed node instance. |
Callback to invoke when a node is collapsed. |
onNodeContextMenuSelect | event.originalEvent: browser event event.node: Selected node instance. |
Callback to invoke when a node is selected with right click. |
Following is the list of structural style classes, for theming classes visit theming page.
Name | Element |
---|---|
ui-tree | Main container element |
ui-tree-horizontal | Main container element in horizontal mode |
ui-tree-container | Container of nodes |
ui-treenode | A treenode element |
ui-treenode-content | Content of a treenode |
ui-treenode-toggler | Toggle icon |
ui-treenode-icon | Icon of a treenode |
ui-treenode-label | Label of a treenode |
ui-treenode-children | Container element for node children |
None.
<p-growl [value]="msgs"></p-growl>
<h3 class="first">Basic</h3>
<p-tree [value]="filesTree1"></p-tree>
<h3>Single Selection</h3>
<p-tree [value]="filesTree2" selectionMode="single" [(selection)]="selectedFile"
(onNodeSelect)="nodeSelect($event)" (onNodeUnselect)="nodeUnselect($event)"></p-tree>
<div style="margin-top:8px">Selected Node: {{selectedFile ? selectedFile.label : 'none'}}</div>
<h3>Multiple Selection with Metakey</h3>
<p-tree [value]="filesTree3" selectionMode="multiple" [(selection)]="selectedFiles"
(onNodeSelect)="nodeSelect($event)" (onNodeUnselect)="nodeUnselect($event)"></p-tree>
<div style="margin-top:8px"Selected Nodes: <span *ngFor="let file of selectedFiles">{{file.label}} </span></div>
<h3>Lazy Loading</h3>
<p-tree [value]="lazyFiles" (onNodeExpand)="nodeExpand($event)" [style]="{'max-height':'200px','overflow':'auto'}"></p-tree>
<h3>Template</h3>
<p-tree [value]="filesTree4" pTemplate type="default">
<template let-node>
<input [(ngModel)]="node.label" type="text" style="width:100%">
</template>
</p-tree>
<h3>Context Menu</h3>
<p-tree [value]="filesTree5" selectionMode="single" [(selection)]="selectedFile2" [contextMenu]="cm"></p-tree>
<p-contextMenu #cm [model]="items"></p-contextMenu>
<h3>Programatic Tree Expansion</h3>
<p-tree #expandingTree [value]="filesTree6"></p-tree>
<div style="margin-top: 8px">
<button pButton type="text" label="Expand all" (click)="expandAll()"></button>
<button pButton type="text" label="Collapse all" (click)="collapseAll()"></button>
</div>
<div style="margin-top: 8px">
<button pButton type="text" label="Expand to 'Invoices.txt'" (click)="expandToNode()"></button>
</div>
<h3>Horizontal Tree</h3>
<p-tree [value]="filesTree7" layout="horizontal" selectionMode="single" [(selection)]="selectedFile" ></p-tree>
<div style="margin-top:8px">Selected Node: {{selectedFile3 ? selectedFile3.label : 'none'}}</div>
export class TreeDemo implements OnInit {
msgs: Message[];
@ViewChild('expandingTree')
expandingTree: Tree;
filesTree1: TreeNode[];
filesTree2: TreeNode[];
filesTree3: TreeNode[];
filesTree4: TreeNode[];
filesTree5: TreeNode[];
filesTree6: TreeNode[];
filesTree7: TreeNode[];
lazyFiles: TreeNode[];
selectedFile: TreeNode;
selectedFile2: TreeNode;
selectedFile3: TreeNode;
selectedFiles: TreeNode[];
items: MenuItem[];
constructor(private nodeService: NodeService) { }
ngOnInit() {
this.nodeService.getFiles().then(files => this.filesTree1 = files);
this.nodeService.getFiles().then(files => this.filesTree2 = files);
this.nodeService.getFiles().then(files => this.filesTree3 = files);
this.nodeService.getFiles().then(files => this.filesTree4 = files);
this.nodeService.getFiles().then(files => this.filesTree5 = files);
this.nodeService.getFiles().then(files => this.filesTree6 = files);
this.nodeService.getFiles().then(files => {
this.filesTree7 = [{
label: 'Root',
children: files
}];
});
this.nodeService.getLazyFiles().then(files => this.lazyFiles = files);
this.items = [
{label: 'View', icon: 'fa-search', command: (event) => this.viewFile(this.selectedFile2)},
{label: 'Unselect', icon: 'fa-close', command: (event) => this.unselectFile()}
];
}
nodeSelect(event) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Node Selected', detail: event.node.label});
}
nodeUnselect(event) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Node Unselected', detail: event.node.label});
}
nodeExpandMessage(event) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Node Expanded', detail: event.node.label});
}
nodeExpand(event) {
if(event.node) {
//in a real application, make a call to a remote url to load children of the current node and add the new nodes as children
this.nodeService.getLazyFiles().then(nodes => event.node.children = nodes);
}
}
viewFile(file: TreeNode) {
this.msgs = [];
this.msgs.push({severity: 'info', summary: 'Node Selected with Right Click', detail: file.label});
}
unselectFile() {
this.selectedFile2 = null;
}
expandToNode(){
const invoicesNode: TreeNode = this.filesTree6[0].children[1].children[0]
this.expandingTree.expandToNode(invoicesNode);
}
expandAll(){
this.filesTree6.forEach( node => {
this.expandRecursive(node, true);
} );
}
collapseAll(){
this.filesTree6.forEach( node => {
this.expandRecursive(node, false);
} );
}
private expandRecursive(node:TreeNode, isExpand:boolean){
node.expanded = isExpand;
if(node.children){
node.children.forEach( childNode => {
this.expandRecursive(childNode, isExpand);
} );
}
}
}