Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

qjon/angular2-tree

Repository files navigation

angular2-tree

Simple component to display tree structure

npm (scoped) Build Status npm version npm npm

Installation

npm i @rign/angular2-tree

Usage

First you have to create your own loader service

@Injectable()
export class AppNodeService extends NodeService {
  public get treeId(): string {
    return 'tree3';
  }
  
  protected apiConfig = {
    addUrl: '/api/nodes',
    getUrl: '/api/nodes',
    updateUrl: '/api/nodes',
    removeUrl: '/api/nodes',
  }
}

and use it to load/save/delete/etc. your node data. Or you can extend and rewrite all methods of that service to store your data wherever you want. See example localStorage.service.ts

Include TreeModule in your application module and create Store with empty state and initialize Effects. Do not forget to pass yours AppNodesService as a parameter of TreeModule.

import {TreeModule} from '@rign/angular2-tree';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    TreeModule.forRoot(AppNodeService),
    EffectsModule.forRoot([]),
    StoreModule.forRoot({})
  ]
})

You need also init translations and animations module, because Tree needs it to translate all labels and animate expanding and collapsing.

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    BrowserAnimationsModule,
    TranslateModule.forRoot(),
    TreeModule.forRoot()
  ]
})

More information about translations you can find below in section Translation.

In any html file put

<ri-tree [treeModel]="treeModel"></ri-tree>

In component where you create tree, you should create TreeModel passing configuration and AppNodeService.

export class MyTreeComponent implements OnInit {
  public folders: Observable<ITreeData>;

  public contextMenu: IContextMenu[] = [];

  public treeConfiguration: IConfiguration = {
    showAddButton: true,
    disableMoveNodes: false,
    treeId: 'tree3',
    dragZone: 'tree3',
    dropZone: ['tree3'],
    isAnimation: true     // add animation to action "expand" and "collapse"
  };

  public treeModel: TreeModel;


  public constructor(private treeInitializerService: TreeInitializerService,
                     private appNodeService: AppNodeService) {
  }

  public ngOnInit(): void {
    const nodes: IOuterNode[] = JSON.parse(localStorage.getItem('treeOne')) || [];

    this.treeModel = this.treeInitializerService.init(this.treeConfiguration, this.appNodeService, nodes);
  }
}

If function init has got third parameter - array of nodes, then the tree will be marked as fully loaded. It will not use load API function to get new subnodes it will use only passed nodes.

CSS Styles

To load default CSS styles and makes our tree looks nice you have to add 2 CSS files to your angular-cli.json file:

...
"styles": [
  "../node_modules/bootstrap/dist/css/bootstrap.css",
  "../node_modules/font-awesome/css/font-awesome.css",
  "../node_modules/@rign/angular2-tree/styles.css",
  "styles.css"
],

Create own item template

Also you can use your own template to display items. You can do that when you extend ItemComponent

@Component({
  selector: 'new-tree-item',
  templateUrl: './newItem.component.html',
  styleUrls: ['./newItem.component.less']
})
export class NewItemComponent extends ItemComponent {

}

and newItem.component.html

<div class="tree-item row"
     [ngClass]="{'tree-item-selected': isSelected}"
     riDroppable
     riDraggable
     [dragZone]="treeModel.configuration.dragZone"
     [dropConfig]="{dropAllowedCssClass: 'drop-enabled', dropZone: treeModel.configuration.dropZone}"
     [data]="node"
     id="node-{{node-id}}"
>
  <div class="col-sm-8">
    <i *ngIf="!isExpanded" (click)="expand()" class="fa fa-plus pointer"></i>
    <i *ngIf="isExpanded" (click)="collapse()" class="fa fa-minus pointer"></i>
    <span *ngIf="!isEditMode" class="tree-item-name" (click)="onSelect()">{{node.name}}</span>
    <form name="form">
      <input #inputElement type="text" class="form-control" *ngIf="isEditMode" [formControl]="nameField"
             name="name" (keydown)="onChange($event)" (blur)="onBlur($event)"/>
    </form>
  </div>
  <div class="col-sm-4 text-right">
      <span class="btn-group btn-group-sm">
        <button class="btn btn-primary" (click)="onEdit($event)" [disabled]="isEditMode">
          <i class="fa fa-edit"></i>
        </button>
        <button class="btn btn-danger" (click)="onDelete()" [disabled]="isEditMode">
          <i class="fa fa-trash"></i>
        </button>
      </span>
  </div>
</div>
<div class="tree" *ngIf="isExpanded" [@expand]>
  <ri-tree-item *ngFor="let child of children$ | async" [node]="child; trackBy: trackByFn"
                [treeModel]="treeModel"
                [isExpanded]="treeModel.isExpanded(child)"
                [isSelected]="treeModel.isSelected(child)"
                [contextMenu]="contextMenu"></ri-tree-item>
</div>

Then when you create tree component in your application use such construction

<rign-tree [treeModel]="treeModel">
  <new-tree-item *ngFor="let node of treeModel.getRootNodes() | async; trackBy: trackByFn" 
                  [node]="node" 
                  [treeModel]="treeModel" 
                  [isSelected]="treeModel.isSelected(node)"
                  [isExpanded]="treeModel.isExpanded(node)"
                  [contextMenu]="contextMenu"></new-tree-item>
</rign-tree>

and that is all. Please see Demo where is such example.

Open initial path

If you would like to open some path at the begin you can do that invoking such method after creating TreeModel.

 this.treeModel.initPath([
   // list of node ids sorted by level of node (grandparent id, parent id, child id)
 ]);

Display parents path

From version 3.0.1 there is possibility to display current selected node path. To do that place in your component html file such code:

<ri-tree-parents-list [treeModel]="treeModel"></ri-tree-parents-list>

The treeModel value is the same object that is used in ri-tree.

Events(Actions)

Using ngrx/store you can listen on below actions and do whatever you want:

TreeActionTypes.TREE_SAVE_NODE
TreeActionTypes.TREE_SAVE_NODE_ERROR
TreeActionTypes.TREE_SAVE_NODE_SUCCESS
TreeActionTypes.TREE_DELETE_NODE
TreeActionTypes.TREE_DELETE_NODE_ERROR
TreeActionTypes.TREE_DELETE_NODE_SUCCESS
TreeActionTypes.TREE_EDIT_NODE_START
TreeActionTypes.TREE_EXPAND_NODE
TreeActionTypes.TREE_LOAD
TreeActionTypes.TREE_LOAD_ERROR
TreeActionTypes.TREE_LOAD_SUCCESS
TreeActionTypes.TREE_LOAD_PATH
TreeActionTypes.TREE_MOVE_NODE
TreeActionTypes.TREE_MOVE_NODE_ERROR
TreeActionTypes.TREE_MOVE_NODE_SUCCESS
TreeActionTypes.TREE_REGISTER
TreeActionTypes.TREE_SET_ALL_NODES
TreeActionTypes.TREE_SELECT_NODE

Translation

Tree module has configured translation for english (default language) and polish. You can add translations for other languages as it is described in Translate Module documentation. In Tree Module you are able to set following labels:

  • RI_TREE_LBL_ADD_NODE - Add node
  • RI_TREE_LBL_EDIT_NODE - Edit node
  • RI_TREE_LBL_REMOVE_NODE - Delete node
  • RI_TREE_LBL_DROP_ZONE - Drop here to move node to root level

To change language to polish you have to add these lines to your app module:

export class AppModule {
  public constructor(translate: TranslateService) {
    translate.use('pl');
  }
}

Drop elements on tree node

Now you have new possibilities to move different elements to the tree (files or other data). To do that, you have to use riDraggable directive in following way

<div ri-draggable [dragZone]="treeModel.configuration.dragZone" [data]="your_data" [sourceType]="'YOUR_SOURCE_TYPE'">Drag element</div>  

where:

  • your_data - is any object
  • YOUR_SOURCE_TYPE - is any type of string which allow you to filter drop effect

Then you have to create @Effects similar to that one in _treeEffects.service_or create only Observable and subscribe to it.

@Effect() move$ = this.actions$
  .ofType(TreeActionTypes.TREE_MOVE_NODE)
  .pipe(
    filter((action: ITreeAction) => {
      return action.payload.sourceOfDroppedData === DragAndDrop.DROP_DATA_TYPE;
    }) 
  )
  ...

but you have to replace

.ofType(TreeActionTypes.TREE_MOVE_NODE)

to

.ofType('YOUR_SOURCE_TYPE')

At the end do not forget to add this effects to your app.

Changes

v3.1.2

  • fix Item template error
  • update bootstrap to 4.1.3

v3.1.0

  • change tree model initialize and injecting NodeService
  • add NestJS server with new TreeTwoNodeBackendService angular service to show how Tree works with real backend (details in Demo section)
  • actions and reducer
    • change events from TreeActionService to TreeActionTypes (the first one will be removed in 4.0.0)
    • rewrite actions from one class to many simpler classes
    • create one type TreeAction which cover all tree actions
  • rewrite TreeItemComponent - improved performance and reduce code
    • move information about expanded nodes from TreeItemComponent to store, these cause that isExpanded is now @Input() property for TreeItemComponent
    • TreeItemComponent has new @Input() property isSelected
    • small changes in expand animation
  • fix issue - when add new node parent node was expanded but not loaded

v3.0.2

  • small fixes with interfaces
  • fix export CSS styles

v3.0.1

  • change the way of injecting NodeService provider
  • save in store: tree root nodes list, tree configuration and selected node
  • display current selected node parents path with navigation
  • add possibility to open path of the tree

v2.3.0

  • fix problem with building tree component in AOT
  • fix few small issues

v2.2.0

  • add forRoot static method
  • change translation module to ng2-translate
  • upgrade angular to verison ^5.0.0
  • upgrade @ngrx/store to version ^4.1.0 (use forFeature to init store and effects)
  • rename selector ri-tree

v2.1.1

  • fix bug with adding new node to root element

v2.1.0

  • add translation module
  • drop elements on tree nodes
  • update and lock of some npm package versions
  • add possibility to animate action collapse and expand nodes of tree, using in configuration property isAnimation: true

v2.0.1

v2.0.0

  • use ngrx/store to store data
  • use actions and effects instead of events
  • add TravisCI configuration
  • remove backend example, move all functionality of demo to local storage

v1.0.0

  • use ngrx/store
  • remove events ITreeItemEvents - use Actions and Effects
  • remove NodeModel
  • simplify using tree

v0.8.1

  • fix package.json

v0.8.0

  • allow to create own template for tree item (if not specify it use default) - look in demo
  • input option disableContextMenu to disable context menu (default: false)
  • update Demo - add alternative view of tree

v0.7.0

  • remove API config service (see section Usage)

v0.6.2

  • change name FolderService to NodeService
  • change params names from dirId to nodeId
  • now you can use in your API paths parameter {nodeId} which will be replaced on nodeId

v0.6.1

  • expose ConfigService - it allow override urls for create, edit, and delete folder

v0.6.0

  • upgrade angular/cli to version beta.32.3
  • fix demo

v0.5.0

  • primary version with all features described below.

Demo

Working demo with local storage you can find here. To run Demo locally clone this repository and run

npm start

If you would like to use demo with real API then you have to make small change. In demo/srx/app/treeTwo.component.ts change injection from TreeTwoNodeService to TreeTwoNodeBackendService.
Then run real backend written in NestJS

cd backend
npm install
npm start

and in second terminal run tree application

npm start

License

Licensed under MIT.

About

Tree component for Angular

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published