# :herb: ng2-tree-pms

ng2-tree is a simple [Angular 2](https://github.com/angular/angular) component for visualizing data that can be naturally represented as a tree.

<!-- TOC -->

- [:clapper: Usage](#clapper-usage)
- [:eyes: Demo](#eyes-demo)
- [:wrench: API](#wrench-api)
  - [tree](#tree)
  - [[tree]](#tree)
    - [Load children asynchronously](#load-children-asynchronously)
    - [Configure node via TreeModelSettings](#configure-node-via-treemodelsettings)
  - [[settings]](#settings)
  - [`Tree` class](#tree-class)
  - [events (nodeMoved, nodeSelected, nodeRenamed, nodeRemoved, nodeCreated)](#events-nodemoved-nodeselected-noderenamed-noderemoved-nodecreated)
    - [NodeSelectedEvent](#nodeselectedevent)
    - [NodeMovedEvent](#nodemovedevent)
    - [NodeRemovedEvent](#noderemovedevent)
    - [NodeCreatedEvent](#nodecreatedevent)
    - [NodeRenamedEvent](#noderenamedevent)
- [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)

<!-- /TOC -->

## :clapper: Usage
Ok, let's start with an installation - all you need to do is:

`npm install --save ng2-tree-pms`

Now when you have `ng2-tree-pms` 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-pms';

@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-pms';

@Component({
  selector: 'myComp',
  // 2 - set [tree] attribute to tree object
  template: `<tree [tree]="tree"></tree>`
})
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`
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: `<tree [tree]="tree" (nodeSelected)="logEvent($event)"></tree>`
})
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) and its [sources](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
    <tree
      [tree]="tree"
      [settings]="settings"
      (nodeRemoved)="handleRemoved($event)"
      (nodeRenamed)="handleRenamed($event)"
      (nodeSelected)="handleSelected($event)"
      (nodeMoved)="handleMoved($event)"
      (nodeCreated)="handleCreated($event)">
    </tree>
```

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-pms';
```

Here is the definition of the `TreeModel` interface:

```typescript
interface TreeModel {
  value: string | RenamableNode;
  children?: Array<TreeModel>;
  loadChildren?: ChildrenLoadingFunction;
  settings?: TreeModelSettings;
  typeModel: string;
}
```

As you can see - object that conforms to this interface has a recursive nature, 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: <RenamableNode>{
              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).

#### 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
  },
  children: [
    {value: 'JavaScript'},
    {value: 'CoffeeScript'},
    {value: 'Lua'},
  ]
}
```

Right now only one option is supported - `static`. 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.
`static` option that's defined on a `parent` is automatically applied to its children. If you don't want to make `static` all the children, then you can override `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)

Here is the diagram that shows tree events' hierarchy

![tree events hierarchy](media/tree-events-hierarchy.png)

`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
    <tree
      [tree]="tree"
      (nodeSelected)="handleSelected($event)">
    </tree>
```

`NodeSelectedEvent` has just one property `node` which contains a `Tree` object representing selected node.

```typescript
{node: <Tree>{...}}
```

#### NodeMovedEvent

You can subscribe to `NodeMovedEvent` by attaching listener to `(nodeMoved)` attribute

```html
    <tree
      [tree]="tree"
      (nodeMoved)="handleMoved($event)">
    </tree>
```

`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: <Tree>{...}, previousParent: <Tree>{...}}
```

#### NodeRemovedEvent

You can subscribe to `NodeRemovedEvent` by attaching listener to `(nodeRemoved)` attribute

```html
    <tree
      [tree]="tree"
      (nodeRemoved)="handleRemoved($event)">
    </tree>
```

`NodeRemovedEvent` has a `node` property, which contains removed node (of type `Tree`).

```typescript
{node: <Tree>{...}}
```

#### NodeCreatedEvent

You can subscribe to `NodeCreatedEvent` by attaching listener to `(nodeCreated)` attribute

```html
    <tree
      [tree]="tree"
      (nodeCreated)="handleCreated($event)">
    </tree>
```

`NodeCreatedEvent` has a `node` property of type `Tree`, which contains a created node.

```typescript
{node: <Tree>{...}}
```

#### NodeRenamedEvent

You can subscribe to `NodeRenamedEvent` by attaching listener to `(nodeRenamed)` attribute

```html
    <tree
      [tree]="tree"
      (nodeRenamed)="handleRenamed($event)">
    </tree>
```

`NodeRenamedEvent` has three properties:

- `node` contains node that was renamed (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: <Tree>{...},
  oldValue: <string|RenamableNode>{...},
  newValue: <string|RenamableNode>{...}
}
```

## 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 easely.

## :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 documentation, please, let me know which parts of it was difficult to grasp and I will improve them.
