Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/valor-software/ng2-tree
Angular tree component
https://github.com/valor-software/ng2-tree
angular angular-tree async branch children ng2-tree tree treeview
Last synced: 4 days ago
JSON representation
Angular tree component
- Host: GitHub
- URL: https://github.com/valor-software/ng2-tree
- Owner: valor-software
- License: mit
- Created: 2015-12-14T11:48:36.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2024-01-30T23:04:24.000Z (11 months ago)
- Last Synced: 2024-11-23T02:08:36.238Z (20 days ago)
- Topics: angular, angular-tree, async, branch, children, ng2-tree, tree, treeview
- Language: TypeScript
- Homepage: http://valor-software.com/ng2-tree/index.html
- Size: 26.2 MB
- Stars: 348
- Watchers: 31
- Forks: 190
- Open Issues: 142
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- awesome-angular-components - ng2-tree - Angular2 component for visualizing data that can be naturally represented as a tree. (Uncategorized / Uncategorized)
- awesome-angular - ng2-tree - Angular2 component for visualizing data that can be naturally represented as a tree. (Uncategorized / Uncategorized)
- awesome-angular-components - ng2-tree - Angular2 Tree component (Uncategorized / Uncategorized)
- awesome-angular-components - ng2-tree - Angular2 component for visualizing data that can be naturally represented as a tree. (Uncategorized / Uncategorized)
- awesome-angular-components - valor-software/ng2-tree - Angular tree component (UI Components / Tree)
- awesome-angular - ng2-tree - Angular tree component. (Table of contents / Third Party Components)
- fucking-awesome-angular - ng2-tree - Angular tree component. (Table of contents / Third Party Components)
- fucking-awesome-angular - ng2-tree - Angular tree component. (Table of contents / Third Party Components)
README
# :herb: ng2-tree
[![npm](https://img.shields.io/npm/v/ng2-tree.svg?style=flat-square)](https://www.npmjs.com/package/ng2-tree)
[![Travis](https://img.shields.io/travis/valor-software/ng2-tree.svg?style=flat-square)](https://travis-ci.org/valor-software/ng2-tree)
[![Codecov](https://img.shields.io/codecov/c/github/valor-software/ng2-tree.svg?style=flat-square)](https://codecov.io/github/valor-software/ng2-tree)* [:clapper: Usage](#clapper-usage)
* [:eyes: Demo](#eyes-demo)
* [:wrench: API](#wrench-api)
* [tree](#tree)
* [[tree]](#tree)
* [Load children asynchronously](#load-children-asynchronously)
* [Load children using ngrx (or any redux-like library)](#load-children-using-ngrx-or-any-redux-like-library)
* [Configure node via TreeModelSettings](#configure-node-via-treemodelsettings)
* [[settings]](#settings)
* [`Tree` class](#tree-class)
* [events (nodeMoved, nodeSelected, nodeRenamed, nodeRemoved, nodeCreated, nodeExpanded, nodeCollapsed)](#events-nodemoved-nodeselected-noderenamed-noderemoved-nodecreated-nodeexpanded-nodecollapsed)
* [NodeSelectedEvent](#nodeselectedevent)
* [NodeMovedEvent](#nodemovedevent)
* [NodeRemovedEvent](#noderemovedevent)
* [NodeCreatedEvent](#nodecreatedevent)
* [NodeRenamedEvent](#noderenamedevent)
* [NodeExpandedEvent](#nodeexpandedevent)
* [NodeCollapsedEvent](#nodecollapsedevent)
* [LoadNextLevelEvent](#loadnextlevelevent)
* [:gun: Controller](#gun-controller)
* [select - selects a node](#select---selects-a-node)
* [isSelected - checks whether a node is selected](#isselected---checks-whether-a-node-is-selected)
* [collapse - collapses a node](#collapse---collapses-a-node)
* [isCollapsed - check whether a node is collapsed](#iscollapsed---check-whether-a-node-is-collapsed)
* [expand - expands a node](#expand---expands-a-node)
* [isExpanded - checks whether a node is expanded](#isexpanded---checks-whether-a-node-is-expanded)
* [toTreeModel - converts a tree to a TreeModel instance](#totreemodel---converts-a-tree-to-a-treemodel-instance)
* [rename - renames a node (changes its value underneath)](#rename---renames-a-node-changes-its-value-underneath)
* [startRenaming - changes the node template so that text input appears and lets a user type a new name](#startrenaming---changes-the-node-template-so-that-text-input-appears-and-lets-a-user-type-a-new-name)
* [remove - removes a node from the tree](#remove---removes-a-node-from-the-tree)
* [addChild - creates a new child node](#addchild---creates-a-new-child-node)
* [changeNodeId - changes node's id](#changenodeid---changes-nodes-id)
* [reloadChildren - loads async children once more](#reloadchildren---loads-async-children-once-more)
* [setChildren - changes children of a node;](#setchildren---changes-children-of-a-node)
* [SystemJS](#systemjs)
* [Changes that should be taken into account in order to migrate from **ng2-tree V1** to **ng2-tree V2**](#changes-that-should-be-taken-into-account-in-order-to-migrate-from-__ng2-tree-v1__-to-__ng2-tree-v2__)
* [:bulb: Want to help?](#bulb-want-to-help)## :clapper: Usage
Ok, let's start with an installation - all you need to do is:
`npm install --save ng2-tree`
Now when you have `ng2-tree` installed, you are in a few steps from having tree in your application:
1. Add the `TreeModule` to your application's module `imports` section:
```typescript
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { BrowserModule } from '@angular/platform-browser';
import { TreeModule } from 'ng2-tree';@NgModule({
declarations: [MyComponent],
imports: [BrowserModule, TreeModule],
bootstrap: [MyComponent]
})
export class MyModule {}
```2. As soon as the previous step is done we need to give ng2-tree a model to render - this can be accomplished by populating its `[tree]` attribute with an object that conforms to the `TreeModel` interface (see [API](#wrench-api)):
```typescript
// 1 - import required classes and interfaces
import { TreeModel } from 'ng2-tree';@Component({
selector: 'myComp',
// 2 - set [tree] attribute to tree object
template: ``
})
class MyComponent {
// 3 - make sure that tree object conforms to the TreeModel interface
public tree: TreeModel = {
value: 'Programming languages by programming paradigm',
children: [
{
value: 'Object-oriented programming',
children: [{ value: 'Java' }, { value: 'C++' }, { value: 'C#' }]
},
{
value: 'Prototype-based programming',
children: [{ value: 'JavaScript' }, { value: 'CoffeeScript' }, { value: 'Lua' }]
}
]
};
}
```3. Apart from that, in order to have usable tree in the browser, you need to add **ng2-tree styles** which you can find in your `node_modules/ng2-tree/styles.css`
In Angular 2/4 cli projects, modify **.angular-cli.json** as below:
```typescript
"styles": [
"styles.css",
"../node_modules/ng2-tree/styles.css"
],
```4. And finally, I suppose, you'd want to listen to events generated by ng2-tree (for a full list of supported events look at the [API](#wrench-api)). No problem, this is also easy to do - for example let's add a listener for `node was selected` kind of events:
```typescript
// 1 - import required classes and interfaces
import { TreeModel, NodeEvent } from 'ng2-tree';@Component({
selector: 'myComp',
// 2 - listent for nodeSelected events and handle them
template: ``
})
class MyComponent {
public tree: TreeModel = { ... };// 3 - print caught event to the console
public logEvent(e: NodeEvent): void {
console.log(e);
}
}
```Voila! That's pretty much it - enjoy :blush:
## :eyes: Demo
Feel free to examine the [demo](https://valor-software.github.io/ng2-tree/index.html) and its [sources](src/demo) to find out how things are wired.
Also, there is [another demo built with Angular CLI](https://github.com/rychkog/ng2-tree-demo).## :wrench: API
Here is the fully stuffed _tree_ tag that you can use in your templates:
```html
```Let's go through every element of this structure one by one.
### tree
`tree` is the selector for `TreeComponent` which is bundled into `TreeModule`:
### [tree]
`tree` has a `[tree]` attribute which needs to be populated with an object implementing `TreeModel` interface. You can import this interface like below:
```typescript
import { TreeModel } from 'ng2-tree';
```Here is the definition of the `TreeModel` interface:
```typescript
interface TreeModel {
value: string | RenamableNode;
id: string | number;
children?: Array;
loadChildren?: ChildrenLoadingFunction;
settings?: TreeModelSettings;
}
```As you can see - an object that conforms to this interface has a recursive nature, an example can be seen below:
```typescript
{
value: 'Programming languages by programming paradigm',
children: [
{
value: 'Object-oriented programming',
children: [
{value: 'Java'},
{value: 'C++'},
{value: 'C#'}
]
},
{
value: 'Prototype-based programming',
children: [
{value: 'JavaScript'},
{value: 'CoffeeScript'},
{value: 'Lua'}
]
}
]
}
```Property `value` can be of type `string` or `RenamableNode`.
`RenamableNode` gives you an additional control over the way node is renamed and rendered (by rendered I mean its text representation). Here is the definition of the `RenamableNode` interface:```typescript
interface RenamableNode {
// This method will be invoked in order to apply new value to this kind of node
setName(name: string): void;// This method will be invoked in order to get a text for rendering as a node value
toString(): string;
}
```Here is an example of such a node in the `TreeModel` object:
```typescript
{
value: 'Programming languages by programming paradigm',
children: [
{
value: 'Object-oriented programming',
children: [
{
// I am a RenamableNode. Yeah, that's me :)
value: {
name: 'Java',
setName(name: string): void {
this.name = name;
},
toString(): string {
return this.name;
}
}
},
{value: 'C++'},
{value: 'C#'}
]
},
{
value: 'Prototype-based programming',
loadChildren: (callback) => {
setTimeout(() => {
callback([
{value: 'JavaScript'},
{value: 'CoffeeScript'},
{value: 'TypeScript'}
]);
}, 5000);
}
}
]
};
```#### Load children asynchronously
Another worth noting thing is `loadChildren`. This function on `TreeModel` allows you to load its **children asynchronously**.
```typescript
{
value: 'Prototype-based programming',
loadChildren: (callback) => {
setTimeout(() => {
callback([
{value: 'JavaScript'},
{value: 'CoffeeScript'},
{value: 'TypeScript'}
]);
}, 5000);
}
}
```Node that defines this function is collapsed by default. At the moment of clicking 'Expand' arrow, it starts loading its children by calling given function.
If `loadChildren` function is given to the node - `children` property is ignored. For more details - have a look at the [Demo](#eyes-demo).#### Load children using ngrx (or any redux-like library)
You can also load children by changing the tree state using ngrx.
The tree can emit an appropriate event notifying you to dispatch a new action in order to load the branch's children.To enable this feature you should set the `TreeModel.emitLoadNextLevel` property to true:
```typscript
const model: TreeModel = {
emitLoadNextLevel : true
}
```Now on the first time the node is expanded a **LoadNextLevelEvent** will be fired (via the **loadNextLevel** EventEmitter in the tree) containing the node that requested a next level (its children) loading.
In your code make sure you change the tree state and add the children to the model.
In addition the regular **NodeExpanded** event will be fired.
**NOTICE**: if both `emitLoadNextLevel` and `loadChildren` are provided, the tree will ignore the `emitLoadNextLevel` and the `LoadNextLevelEvent` won't be fired.
#### Configure node via TreeModelSettings
Apart from that `TreeModel` interface has an optional field called `settings` of type `TreeModelSettings`.
Here is an example of its usage:
```typescript
{
value: 'Prototype-based programming',
settings: {
'static': true,
'rightMenu': true,
'leftMenu': true,
'cssClasses': {
'expanded': 'fa fa-caret-down fa-lg',
'collapsed': 'fa fa-caret-right fa-lg',
'leaf': 'fa fa-lg',
'empty': 'fa fa-caret-right disabled'
},
'templates': {
'node': '',
'leaf': '',
'leftMenu': ''
},
'menuItems': [
{ action: NodeMenuItemAction.Custom, name: 'Foo', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Bar', cssClass: 'fa fa-arrow-right' },
{ action: NodeMenuItemAction.Custom, name: 'Baz', cssClass: 'fa fa-arrow-right'}
]
}
},
children: [
{value: 'JavaScript'},
{value: 'CoffeeScript'},
{value: 'Lua'}
]
}
```* `static` - Boolean - This option makes it impossible to drag a tree or modify it in a some way, though you still can select nodes in the static tree and appropriate events will be generated.
* `isCollapsedOnInit` - Boolean - This option makes a tree to be collapsed on first load (this option cascades to its children).
* `rightMenu` - Boolean - This option allows you to activate (true, by default) or deactivate (false) right menu when clicking with right button of a mouse.
* `leftMenu` - Boolean - This option allows you to activate (true) or deactivate (false, by default) left menu.
* `cssClasses` - Object:
* `expanded` - String - It specifies a css class (or classes) for an item which represents expanded state of a node. The item is clickable and it transitions the node to the collapsed state
* `collapsed` - String - It specifies a css class (or classes) for an item which represents collapsed state of a node. The item is clickable and it transitions the node to the expanded state
* `leaf` - String - It specifies a css class (or classes) for an item which represents a node without an option to expand or collapse - in other words: a leaf node.
* `empty` - String - Node is considered empty when it has no children. Once this condition is satisfied - appropriate css class will be applied to the node.
* `templates` - Object:
* `node` - String - It specifies a html template which will be included to the left of the node's value.
* `leaf` - String - It specifies a html template which will be included to the left of the leaf's value.
* `leftMenu` - String - It specifies a html template to the right of the node's value. This template becomes clickable and shows a menu on node's click.
* `menuItems` - here you can specify your custom menu items. You should feed an array of NodeMenuItem instances to this setting. Once done - setup a subscription to `MenuItemSelectedEvent`s by listening to `(menuItemSelected)="onMenuItemSelected($event)"` on the tree.All options that are defined on a `parent` are automatically applied to children. If you want you can override them by `settings` of the child node.
### [settings]
Object that should be passed to `[settings]` must be of type [`Ng2TreeSettings`](src/tree.types.ts). This attribute is **optional**. Right now only one setting is available in there - `rootIsVisible`. This setting allows you to make a root node of the tree _invisible_:
```typescript
const treeSettings: Ng2TreeSettings = {
rootIsVisible: false
};
```By default `rootIsVisible` equals to `true`
### `Tree` class
Also in the next section, you'll be reading about events generated by the `ng2-tree`. And here [Tree](src/tree.ts) class comes in handy for us, because its instances propagated with event objects. Under the hood, `ng2-tree` wraps a `TreeModel` provided by the user in `Tree`. And `Tree` in turn has lots of useful methods and properties (like `parent`, `hasChild()`, `isRoot()` etc.)
### events (nodeMoved, nodeSelected, nodeRenamed, nodeRemoved, nodeCreated, nodeExpanded, nodeCollapsed)
`NodeEvent` is the root of the tree events' hierarchy. It defines property `node` that contains a receiver of the event action (`node` is an instance of the `Tree` class).
`NodeDestructiveEvent` is the parent for all events that cause changes to the structure of the tree or to the node's value.
#### NodeSelectedEvent
You can subscribe to the `NodeSelectedEvent` by attaching listener to the `(nodeSelected)` attribute
```html
````NodeSelectedEvent` has just one property `node` which contains a `Tree` object representing selected node.
```typescript
{node: {...}}
```#### NodeMovedEvent
You can subscribe to `NodeMovedEvent` by attaching listener to `(nodeMoved)` attribute
```html
````NodeMovedEvent` has two properties `node` and `previousParent` both of which contain `Tree` objects:
* `node` contains a moved node;
* `previousParent` contains a previous parent of the moved node;```typescript
{node: {...}, previousParent: {...}}
```#### NodeRemovedEvent
You can subscribe to `NodeRemovedEvent` by attaching listener to `(nodeRemoved)` attribute
```html
````NodeRemovedEvent` has a `node` property, which contains removed node (of type `Tree`).
```typescript
{node: {...}}
```#### NodeCreatedEvent
You can subscribe to `NodeCreatedEvent` by attaching listener to `(nodeCreated)` attribute
```html
````NodeCreatedEvent` has a `node` property of type `Tree`, which contains a created node and a `controller` property, which will give you access to node's controller.
```typescript
{node: {...}}
```#### NodeRenamedEvent
You can subscribe to `NodeRenamedEvent` by attaching listener to `(nodeRenamed)` attribute
```html
````NodeRenamedEvent` has three properties:
* `node` contains a node that was renamed ( an instance of `Tree`).
* `oldValue` contains a value, that node used to have (it might be `string` or `RenamableNode`)
* `newValue` contains a new value of the node (it might be `string` or `RenamableNode`)```typescript
{
node: {...},
oldValue: {...},
newValue: {...}
}
```#### NodeExpandedEvent
You can subscribe to `NodeExpandedEvent` by attaching listener to `(nodeExpanded)` attribute, this event wont fire on initial expansion
```html
````NodeExpandedEvent` has a `node` property of type `Tree`, which contains an expanded node.
```typescript
{node: {...}}
```#### NodeCollapsedEvent
You can subscribe to `NodeCollapsedEvent` by attaching listener to `(nodeCollapsed)` attribute
```html
````NodeCollapsedEvent` has a `node` property of type `Tree`, which contains a collapsed node.
```typescript
{node: {...}}
```#### LoadNextLevelEvent
You can subscribe to `LoadNextLevelEvent` by attaching a listener to `(loadNextLevel)` attribute.
Relevant for loading children via ngrx (or any redux-inspired library).```html
````LoadNextLevelEvent` has a `node` property of the type `Tree`, which contains a node for which next level (its children) should be loaded.
```typescript
{node: {...}}
```## :gun: Controller
First of all you should know how to get a controller of a particular node. You can get a controller of a node only if you set an id property of a node.
> TIP: Ids for nodes created via the context menu or using a TreeController instance get populated automatically unless nodes had ids before there were added to the tree
For example, your tree structure should look like:
```typescript
public tree: TreeModel = {
value: 'Programming languages by programming paradigm',
id: 1,
children: [
{
value: 'Object-oriented programming',
id: 2,
children: [
{value: 'Java', id: 3},
{value: 'C++', id: 4},
{value: 'C#', id 5},
]
},
{
value: 'Prototype-based programming',
id: 6,
children: [
{value: 'JavaScript', id: 7},
{value: 'CoffeeScript', id: 8},
{value: 'Lua', id: 9},
]
}
]
};
```Ids must be unique within a one tree, otherwise, some controllers will be overwritten and you won't be able to acquire them.
In order to get a node's controller you need to create an Angular local variable out of tree component via hash binding in the template:```typescript
@Component({
template: ''
})
class TheComponent implements AfterViewInit {
tree: TreeModel = {
value: 'Programming languages by programming paradigm',
id: 1,
children: [
{
value: 'Object-oriented programming',
id: 2,
children: [
{value: 'Java', id: 3},
{value: 'C++', id: 4},
{value: 'C#', id 5},
]
},
{
value: 'Prototype-based programming',
id: 6,
children: [
{value: 'JavaScript', id: 7},
{value: 'CoffeeScript', id: 8},
{value: 'Lua', id: 9},
]
}
]
};@ViewChild('treeComponent') treeComponent;
ngAfterViewInit(): void {
// ... make use of this.treeComponent ...
}
}
```then by executing `this.treeComponent.getControllerByNodeId(PUT_HERE_YOUR_NODE_ID)` you'll get an instance of a TreeController (another couple steps and the world is yours =) )
Below are more detailed explanations of the TreeController and its usage. Let's go method by method:
```typescript
const oopNodeController = this.treeComponent.getControllerByNodeId(2);
```#### select - selects a node
```typescript
oopNodeController.select();
```This method selects the node and unselects all the other nodes, also it fires a select event.
#### isSelected - checks whether a node is selected
```typescript
oopNodeController.isSelected();
```This method returns true if the node is selected and false if it isn't.
#### collapse - collapses a node
```typescript
oopNodeController.collapse();
```This method collapses a node if the node is collapsible (for example we cannot collapse a leaf). If the node gets collapsed successfully - a collapse event gets fired.
#### isCollapsed - check whether a node is collapsed
```typescript
oopNodeController.isCollapsed();
```This method returns true if the node is collapsed and false otherwise.
#### expand - expands a node
```typescript
oopNodeController.expand();
```This method expands the node in case it can be expanded. On successful expanding the expand event is fired.
#### expandToParent - expands a node and its parents up to the root
```typescript
oopNodeController.expandToParent();
```This method expands the node even if it is a leaf. Expand event is fired for every expanded parent up to the root.
**Important:** For this to work - `keepNodesInDOM: true` should be set on the appropriate tree.
#### isExpanded - checks whether a node is expanded
```typescript
oopNodeController.isExpanded();
```#### toTreeModel - converts a tree to a TreeModel instance
Actually controller makes and returns a clone of tree's underlying model
```typescript
oopNodeController.toTreeModel();
```This method returns true if the node is expanded and false otherwise.
#### rename - renames a node (changes its value underneath)
```typescript
oopNodeController.rename('new value');
```This method accepts a string and sets it as a node's new value, this action also fires rename event.
#### startRenaming - changes the node template so that text input appears and lets a user type a new name
```typescript
oopNodeController.startRenaming();
```After the user entered the new name a rename event will be fired.
#### remove - removes a node from the tree
```typescript
oopNodeController.remove();
```This method removes the node and its children and fires remove event.
#### addChild - creates a new child node
```typescript
let newNode: TreeModel = {
value: 'Go',
children: []
};
oopNodeController.addChild(newNode);
```This method accepts a TreeModel and adds it as a child of the parent or as a sibling (depends on which controller this was called - branch controller or a leaf controller).
### changeNodeId - changes node's id
```typescript
oopNodeController.changeNodeId(10);
```This method can change a node's id. When the user creates a node from node's menu you will access the new node after it's created and this method will provide a way to change the node's id.
### reloadChildren - loads async children once more
```typescript
oopNodeController.reloadChildren();
```### setChildren - changes children of a node;
```typescript
let newChildren: Array = [
{ value: 'new children 1' },
{ value: 'new children 2' },
{ value: 'new children 3' }
];
oopNodeController.setChildren(newChildren);
```This method replaces all existing children of the node with new ones.
## SystemJS
If you are using SystemJS, then you need
```javascript
System.config({
// ...
map: {
// ...
'ng2-tree': 'node_modules/ng2-tree/bundles/ng2-tree.umd.min.js',
// ...
},
// ...
}
```## Changes that should be taken into account in order to migrate from **ng2-tree V1** to **ng2-tree V2**
* Events were reworked:
* In V1 all events that were inherited from NodeDestructiveEvent used to have property `parent`. It's not the case anymore. If you need a parent you should get it from `node` in event object like `node.parent`;
* All events used to have `node` property of type `TreeModel`. Now `node` is of type [Tree](#tree-class) (as well as `node.parent`);
* `NodeMovedEvent` now has property `previousParent`, which contains tree in which moved node used to be.
* CSS styles in **ng2-tree V2** are distributed as separate file which you can find in `node_modules/ng2-tree/styles.css`. That allows you to override ng2-tree styles more easily.## :bulb: Want to help?
I am very appreciate for your ideas, proposals and found bugs which you can put in [github issues](https://github.com/valor-software/ng2-tree/issues). Thanks in advance!
**P.S.** If you find it hard going through the documentation, please, let me know which parts of it was difficult to grasp and I will improve them.