let tree = {
draggableOptions: {group: 'share'},
label: 'root',
selected: false,
nodes: [
{
label: 'item1',
selected: false,
nodes: [
{
label: 'item1.1',
selected: false
},
{
label: 'item1.2',
selected: false,
nodes: [
{
label: 'item1.2.1',
selected: false
}
]
}
]
},
{
label: 'item2',
selected: false
}
]
}
Vue.component('tree-menu', {
template: '#tree-menu',
props: [ 'nodes', 'label', 'depth', 'selected' ],
data() {
return {
showChildren: false
}
},
computed: {
iconClasses() {
return {
'fa-plus-square-o': !this.showChildren,
'fa-minus-square-o': this.showChildren
}
},
labelClasses() {
return { 'has-children': this.nodes }
},
indent() {
return { transform: `translate(${this.depth * 50}px)` }
}
},
methods: {
toggleChildren() {
this.showChildren = !this.showChildren;
},
tickChildren(ev) {
this.selected = !this.selected;
this.tickRecursive(this);
},
tickRecursive(node) {
if(node.nodes && node.nodes.length)
node.nodes.forEach(x => {
x.selected = this.selected;
this.tickRecursive(x);
});
}
}
});
new Vue({
el: '#app',
data: {
tree
}
})
body {
font-family: "Open Sans", sans-serif;
font-size: 18px;
font-weight: 300;
line-height: 1em;
}
.container {
padding-left: 5rem;
padding-right: 5rem;
margin: 0 auto;
}
.tree-menu {
.label-wrapper {
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #ccc;
.has-children {
cursor: pointer;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js">
</script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="84f7ebf6f0e5e6e8e1eef7c4b5aab3aab4">[email protected]</a>/Sortable.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.16.0/vuedraggable.min.js"></script>
<div class="container">
<h4>Implementation of Tree Structure with checkboxes and drag-and-drop functionality in Vue<br/><small>(Using Recursive Components and Vue Draggable)</small><br /><small><a href="https://stackoverflow.com/questions/60260675/implement-tree-with-checkboxes-and-drag-drop" target="_blank">Refer to StackOverflow question</a></small></h4>
<div id="app">
<tree-menu
:nodes="tree.nodes"
:depth="0"
:label="tree.label"
:selected="tree.selected"
></tree-menu>
</div>
</div>
<script type="text/x-template" id="tree-menu">
<div class="tree-menu">
<div class="label-wrapper">
<div :style="indent" :class="labelClasses" @click.stop="toggleChildren">
<i v-if="nodes" class="fa" :class="iconClasses"></i>
<input type="checkbox" :checked="selected" @input="tickChildren" @click.stop />
{{label}}
</div>
</div>
<draggable v-model="nodes" :options="{group:{ name:'g1'}}">
<tree-menu
v-if="showChildren"
v-for="node in nodes"
:nodes="node.nodes"
:label="node.label"
:depth="depth + 1"
:selected="node.selected"
:key="node"
>
</tree-menu>
</draggable>
</div>
</script>