Drag & Drop
Demo
Options
Event
Override default behaviour
Drag a node outside of the tree
Drag an external element into the tree
Drag to an empty tree
Mobile
Styling
Demo
Options
options = {
allowDrag: (node) => node.isLeaf,
allowDrop: (element, { parent, index }) {
// return true / false based on element, to.parent, to.index. e.g.
return parent.hasChildren;
},
getNodeClone: (node) => ({
...node.data,
id: uuid.v4(),
name: `copy of ${node.data.name}`
})
};
options2 = {
allowDrag: true,
allowDrop: false
};
<tree-root
[nodes]="nodes"
[options]="options"></tree-root>
Dragging:
Enable dragging with allowDrag
option (default false) accepts either a boolean or a function:
- Boolean value - decides if drop is allowed or not on the tree
- Function - decides on a per node basis if drop is allowed. The function receives:
- node - the dragged TreeNode
Dropping:
Enable selective dropping with the allowDrop
option (default true) accepts either a boolean or a function:
- Boolean value - decides if drop is allowed or not on the tree
- Function - decides on a per node basis if drop is allowed. The function receives:
- element - the dragged element
- to - drop location structure
- parent - the parent node
- index - the index inside the parent's children where the element is dropped
Copying:
The default behaviour of the tree is to copy the node when ctrl
is pressed while dropping the node
- By default the tree will shallow-clone the node data and generates a random ID
- if
getNodeClone
option is supplied, it will be called to get a copy of the node. It receives a TreeNode object, and should return a node object (only the data).
Event
You can listen to moveNode
events and get the moved node, and its new location in the tree
options = {
allowDrag: true
}
onMoveNode($event) {
console.log(
"Moved",
$event.node.name,
"to",
$event.to.parent.name,
"at index",
$event.to.index);
}
<tree-root
(moveNode)="onMoveNode($event)"
[nodes]="nodes"
[options]="options"></tree-root>
Override default behaviour
use actionMapping.mouse.drop
to override the default drag behaviour.
You can also listen to all other drag events like dragEnd, dragLeave etc.:
options = {
allowDrag: true,
actionMapping: {
mouse: {
drop: (tree:TreeModel, node:TreeNode, $event:any, {from, to}) => {
// use from to get the dragged node.
// use to.parent and to.index to get the drop location
// use TREE_ACTIONS.MOVE_NODE to invoke the original action
},
dragStart?: IActionHandler,
drag?: IActionHandler,
dragEnd?: IActionHandler,
dragOver?: IActionHandler,
dragLeave?: IActionHandler,
dragEnter?: IActionHandler
}
}
}
<tree-root
[nodes]="nodes"
[options]="options"></tree-root>
In the drop callback, you get a 'from' and 'to' objects:
-
from:
- If dragging a node, then from === the dragged node
- If dragging something else, it is the draggedElement (see treeDrag directive below)
-
to:
- parent: the parent node
- index: the index inside the parent's children where the node should be dropped
- dropOnNode: distinguish between dropping on the node itself or the drop slot
Drag and drop between trees
This is enabled by default when dragging is enabled
Drag a node outside of the tree
You can use the (treeDrop) directive to allow dragging nodes to an external element.
For example:
<div (treeDrop)="onDrop($event)"
[treeAllowDrop]="allowDrop.bind(this)"></div>
onDrop($event) {
// Dropped $event.element
}
allowDrop(element) {
// Return true/false based on element
}
Use $event.element
inside the callback.
Use [treeAllowDrop]
Input to specify a function that runs onDragOver, and decides if the dropping is allowed or not.
Drag an external element into the tree
You can use the [treeDrag]
directive to allow dragging external elements into the tree.
Then use a custom action to handle the drop (see Action Mapping section).
For example:
options = {
actionMapping: {
mouse: {
drop: (tree, node, $event, {from, to}) => {
console.log('drag', from, to); // from === {name: 'first'}
// Add a node to `to.parent` at `to.index` based on the data in `from`
// Then call tree.update()
}
}
}
}
<p [treeDrag]="{name: 'first'}" [treeDragEnabled]="true">Drag me!</p>
<p [treeDrag]="{name: 'second'}">Drag me as well!</p>
The data that you pass to [treeDrag]
will be passed to the handler in the from
parameter.
Use to.parent
and to.index
to get the drop location.
Use to.dropOnNode
to distinguish between dropping on the node itself or the drop slot.
If you add a new node to the tree, you'll need to call tree.update()
afterwards.
Use [treeDragEnabled]
boolean Input to decide if the drag is enabled or not.
Drag to an empty tree
To drag to an empty tree, set your nodes to an empty array.
If nodes is undefined or null, the drop slot will not appear.
Mobile
To support drag and drop on mobile, you can use DragDropTouch polyfill in your project to enable it.
Download the code from here: https://github.com/Bernardo-Castilho/dragdroptouch
Place it somewhere in your code, and import it from polyfills.ts, or just place a script tag to include it.
Styling
The following classes are available for dragging over elements, based on allowDrop:
- is-dragging-over
- is-dragging-over-disabled
You can disable those classes completely by setting the allowDragoverStyling
option to false.
Performance Issues
It there are performance issues on large trees while drag and drop it's possible to detach change detection during the drag.
// use private cdr: ChangeDetectorRef in constructor
let treeOptions: ITreeOptions = {
actionMapping: {
mouse: {
dragStart: () => { this.cdr.detach(); },
dragEnd: () => { this.cdr.reattach(); },
}
}
};
Updated less than a minute ago