Simple component to display tree structure
npm i @rign/angular2-tree
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);
}
}
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"
>
<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.
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
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(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.
- 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
Licensed under MIT.