Simple component to display tree structure
npm i @rign/angular2-tree
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.
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"
],
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.
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)
]);
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.
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
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');
}
}
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.
- fix Item template error
- update bootstrap to 4.1.3
- 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
- small fixes with interfaces
- fix export CSS styles
- 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
- fix problem with building tree component in AOT
- fix few small issues
- 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
- fix bug with adding new node to root element
- 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
- add MIT LICENSE
- 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
- use ngrx/store
- remove events ITreeItemEvents - use Actions and Effects
- remove NodeModel
- simplify using tree
- fix package.json
- 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
- remove API config service (see section Usage)
- 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
- expose ConfigService - it allow override urls for create, edit, and delete folder
- upgrade angular/cli to version beta.32.3
- fix demo
- primary version with all features described below.
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
Licensed under MIT.