用vue递归组件写树形控件
最近移动端H5项目有树级选择需求,网上没有找到好用的插件,于是参照着网上的教程和Vue官网自己写了一个。效果如图所示:
组件在它的模板内可以递归地调用自己,只有当它有 name
选项时才可以。
模拟数据格式如下:
dataList = [{
nodeName: "电能耗模型",
child: [{
nodeName: '园区',
child: [{
nodeName: "新华科技园"
},
{
nodeName: "百度大厦"
}
]
}]
},
{
nodeName: "电费统计",
child: [{
nodeName: '园区',
child: [{
nodeName: "新华科技园"
},
{
nodeName: "百度大厦"
}
]
}]
}
]
子组件
官网强调了子组件name的重要性,所以name必须和子组件名一样才可以递归调用。
<p-tree v-for='(cel, index) in model.children' :model='cel' :key="index"></p-tree>
export default {
name: 'pTree'
}
定义一个open和一个isFolder变量。open判断该组件是否是打开状态,isFolder根据是否存在子组件数据或者子组件的数据长度,处理是否显示箭头,是否是展开的箭头。因为子组件数据不是固定的,所以isFolder需要写到计算属性里。
data() {
return {
open: false
}
},
computed: {
isFolder() {
return this.model.children && this.model.children.length
}
},
显示隐藏事件
toggle() {
if (this.isFolder) {
this.open = !this.open;
}
}
<template>
<li>
<div>
<i @click='toggle' v-if='isFolder' class="iconfont" :class="[open?'icon-jiantou-copy':'icon-jiantou']"></i>
<i v-else class="iconfont icon-jiantou-noFloder-copy"></i>
<span @click="sendChildVal(model)">
{{model.nodeName}}
</span>
</div>
<ul v-show="open" v-if='isFolder'>
<p-tree v-for='(cel, index) in model.children' :model='cel' :key="index"></p-tree>
</ul>
</li>
</template>
<script>
export default {
name: 'pTree',
props: ['model', 'treeVal'],
components: {},
data() {
return {
open: false
}
},
computed: {
isFolder() {
return this.model.children && this.model.children.length
}
},
methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open;
}
},
sendChildVal(model){
this.bus.$emit('treeVal', model)
}
}
}
</script>
父组件
父组件外层是ul,根据数据的内容进行循环嵌套pTree 子组件。
<template>
<ul v-for="(item, index) in dataList " :key="index">
<pTree :model='item'></pTree>
</ul>
</template>
<script>
import pTree from "./pTree.vue"
components: {
pTree
}
}
</script>
传值问题:
通过简单的props和$emit进行传值的话,你会发现只有第一级树形能返回值,因为除了第一级,其他的都是属于子组件的子组件,并非父子关系。
这里我们使用总线传值的方法,在根实例下为每个实例添加一个bus属性。
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
Vue.prototype.bus = new Vue()
子组件传值:
<span @click="sendChildVal(model)">
{{model.nodeName}}
</span>
methods: {
sendChildVal(model){
this.bus.$emit('treeVal', model)
}
}
父组件接收:
data() {
return {
ptreeVal: {}
}
}
mounted() {
this.bus.$on('treeVal', val => {//通过$on监听事件treeVal
this.ptreeVal = val
})
}