Element-ui 是现在国内用得比较多的ui框架,其table组件提供了多选以及树形数据的功能,但却没有提供将两者结合的功能,我们先来看下面的图片,本文将介绍以下的功能如何实现以及其中的一些踩坑点。
在上面的Gif中,当我们点击高层级的checkbox时,低层级的checkbox也会一并被勾选。取消的时候则一起被取消。要实现这个功能,我们首先得关注待渲染的数据结构。
tableData: [{
id: 1,
date: '2016-05-02',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1518 弄'
}, {
id: 2,
date: '2016-05-04',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1517 弄'
}, {
id: 3,
date: '2016-05-01',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1519 弄',
children: [{
id: 31,
date: '2016-05-01',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1519 弄'
}, {
id: 32,
date: '2016-05-01',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1519 弄',
children: [{
id: 61,
date: '2016-05-01',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1519 弄'
}, {
id: 62,
date: '2016-05-01',
name: '王小虎',
checked: false,
address: '广州市普陀区金沙江路 1519 弄'
}]
}]
}, {
id: 4,
date: '2016-05-03',
name: '王小虎',
checked: false,
address: '上海市普陀区金沙江路 1516 弄'
}]
每一行待渲染的数据都有其对应的checked,可以通过这个key绑定对应的checkbox,有一些数据有children,其为数组形式,这就是待渲染的子树,数组内的对象同样有checked属性,也同样可以有children属性(非必须),当然实际开发中数据结构不一定这么简单,这时就必须通过改变数据结构来改造数组了,个人认为数据结构是一个优秀的程序员必须了解和学习的。
使用template可以自定义我们想要的内容,我们可以通过对应数据的checked绑定每一行的checkbox,使用slot="header"可以自定义表头,因此最终我们可以写出以下代码
<el-table-column align="center">
<template slot="header">
<el-checkbox v-model="checkedAll"
@change="handleCheckedAll"/>
</template>
<template slot-scope="scope">
<el-checkbox v-model="scope.row.checked"
@change="handleCheckedOne(scope.row)"/>
</template>
</el-table-column>
checkedAll用于与表头绑定,handleCheckedAll和handleCheckedOne(scope.row)足够我们完成后续的功能,但你会发现一个问题,表头的checkbox无法被勾选,控制台也不会报错,解决方法是在slot="header"后添加slot-scope="scope",同时将scope传入handleCheckedAll,即使传入的scope并不需要用到。
当我们勾选父级checkbox时,对应的子级checkbox也执行了相同的逻辑,子级可以还会有自己的子级,在其下面可能还存在着子级。这时,你很容易想到递归,事实上递归在解决树类问题确实实用性很高。
// boolValue接收一个布尔值
checkOne(obj, boolValue) {
obj.checked = boolValue
// 判断存不存在children,存在就递归赋值
if (obj.children) {
obj.children.forEach(item => this.checkOne(item, boolValue))
}
},
// 绑定每一行checkbox的change事件
handleCheckedOne(row) {
if (row.checked) {
this.checkOne(row, true)
} else {
this.checkOne(row, false)
}
}
当我们要点击表头的checkbox来改变所有的行的checkbox的时候,我们要改变的就是数组了。
checkAll(arr, boolValue) {
arr.forEach(item => {
item.checked = boolValue
// children同样是数组
if (item.children) {
this.checkAll(item.children, boolValue)
}
})
},
handleCheckedAll(scope) {
// 实际上scope并没有什么用处,仅为了解决上面提到的表头checkbox不显示的bug
// console.log(scope.column)
if (this.checkedAll) {
this.checkAll(this.tableData, true)
} else {
this.checkAll(this.tableData, false)
}
}
遍历每一行执行 checkOne() 也是一种实现 checkAll() 的做法,但你还得处理最外层数据的checkbox,个人认为这样反而使得代码逻辑不清晰,泛用性差。
递归在为每一个节点添加checked以及获取上述数据的id都有使用到,本文仅展示其在父子树关联这方面的作用。如果我的文章帮到了你,请给我点个赞吧!!!