0π
I donβt think you need nested component, it will make the logic complex.
Below is my solution for lazy load one node (expand):
-
add one prop = actions, which allow parent component/view register its event, then bind to the click event.
-
parent component/view defines its lazy load function, then bind
<item :actions="{your lazy load function}">
You can do the similar thing for the selected.
The codes will be like below:
var demoData = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
Vue.component('item', {
template: '#item-template',
props: {
model: Object,
'actions': {
type: Object,
default: ()=>{ return {
'loadNode': function(clickedObj){console.log('default',clickedObj)},
'selectedChange': function(node){console.log('default',node)}
}
}
}
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
this.$emit("expand") // doesn't work properly
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
app = new Vue({ //not vue, it is Vue
el: "#app",
data: {
treeData: demoData,
selected: {}
},
methods: {
myLoadNode: function (parent) {
setTimeout(()=>{
if(!parent.children) {
this.$set(parent, 'children', [])
}
parent.children.push(
{
name: 'ajax folder',
children: [
{ name: 'ajax a1' },
{ name: 'ajax a2' }
]
}
)
}, 500)
},
mySelectedChange: function (node) {
// if each node has unique id, use
// this.$set(this.selected, node.id, true) will be better
this.$set(this.selected, JSON.stringify(node), true)
}
}
})
body {
font-family: Menlo, Consolas, monospace;
color: #444;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: dot;
}
.load-button1{
background-color:red
}
.load-button2{
background-color:yellow
}
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
@click="toggle"
@dblclick="changeType"
>
<a class="load-button1" @click="actions.loadNode(model)">Load</a>
<a class="load-button2" @click="actions.selectedChange(model)">Select</a>
{{ model.name }}
<span v-if="isFolder">[{{ open ? '-' : '+' }}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model"
:actions="{'loadNode':actions.loadNode, 'selectedChange':actions.selectedChange}"
>
</item>
<li class="add" @click="addChild">+</li>
</ul>
</li>
</script>
<div id="app">
<item
class="item"
:model="treeData"
:actions="{'loadNode':myLoadNode, 'selectedChange': mySelectedChange}">
</item>
<p>{{selected}}
</div>
Source:stackexchange.com