/angular2-tree

Tree component for Angular

Primary LanguageTypeScriptMIT LicenseMIT

angular2-tree

Simple component to display tree structure

npm (scoped) Build Status npm version npm npm

Installation

npm i @rign/angular2-tree

Usage

Include TreeModule in your application module and create Store with empty state and initialize Effects.

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

@NgModule({
  declarations: [
    ...
  ],
  imports: [
    ...
    TreeModule.forRoot(),
    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,
    TranslationModule.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>

Create your own loader service as it is done in example

@Injectable()
export class AppNodeService extends NodeService {
  protected apiConfig = {
    addUrl: '/api/nodes',
    getUrl: '/api/nodes',
    updateUrl: '/api/nodes',
    removeUrl: '/api/nodes',
  }
}

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

In component where you create tree, you should register tree store, create TreeModel and load root tree

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 store: Store<ITreeState>,
                     private treeActions: TreeActionsService,
                     private nodeDispatcherService: NodeDispatcherService,
                     private appNodeService: AppNodeService) {
  }

  public ngOnInit() {
    const treeId = this.treeConfiguration.treeId;
    this.nodeDispatcherService.register(treeId, this.appNodeService);

    this.store.dispatch(this.treeActions.registerTree(treeId));

    this.folders = this.store.select(treeStateSelector)
      .map((data: ITreeState) => {
        return data[treeId];
      })
      .filter((data: ITreeData) => !!data)
    ;
    this.treeModel = new TreeModel(this.folders, this.treeConfiguration);
  }
}

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"
>
  <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" [@isExpanded]="animationState" (@isExpanded.done)="onAnimationDone($event)">
  <div *ngIf="isExpanded">
    <new-tree-item  *ngFor="let child of children$ | async" [node]="child" [treeModel]="treeModel" [contextMenu]="contextMenu"></new-tree-item>
  </div>
</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" [node]="node" [treeModel]="treeModel" [contextMenu]="contextMenu"></new-tree-item>
</rign-tree>

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

Events(Actions)

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

TreeActionsService.TREE_SAVE_NODE
TreeActionsService.TREE_SAVE_NODE_SUCCESS
TreeActionsService.TREE_SAVE_NODE_ERROR
TreeActionsService.TREE_DELETE_NODE
TreeActionsService.TREE_DELETE_NODE_SUCCESS
TreeActionsService.TREE_DELETE_NODE_ERROR
TreeActionsService.TREE_EDIT_NODE_START
TreeActionsService.TREE_EXPAND_NODE
TreeActionsService.TREE_LOAD
TreeActionsService.TREE_LOAD_SUCCESS
TreeActionsService.TREE_LOAD_ERROR
TreeActionsService.TREE_MOVE_NODE
TreeActionsService.TREE_MOVE_NODE_SUCCESS
TreeActionsService.TREE_MOVE_NODE_ERROR
TreeActionsService.TREE_REGISTER

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(TreeActionsService.TREE_MOVE_NODE)
  .pipe(
    filter((action: ITreeAction) => {
      return action.payload.sourceOfDroppedData === DragAndDrop.DROP_DATA_TYPE;
    }) 
  )
  ...

but you have to replace

.ofType(TreeActionsService.TREE_MOVE_NODE)

to

.ofType('YOUR_SOURCE_TYPE')

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

Changes

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

License

Licensed under MIT.