el-table选中所有数据(包括非当前页的数据)的实现
抛出问题
在日常使用table表格的时候,我们一般会分页来加载数据,不过有些时候又需要选中所有的数据来进行操作,例如:
- 选中满足筛选条件后的商品来参加活动
- 选中所有的记录来一次性删除
- 编辑参加活动的商品时,把原本已参加活动的商品勾选上
在刚接触到这个需求的时候,我的第一反应是做不了,数据都没获取到怎么选中他嘛。后来有小伙伴建议让后端新定义一个字段,如isAll,点击了选中所有按钮后给后端传一个true告知其选中所有的数据。
不过这样也有几个问题:
- 点击按钮后,我们的table表格上的选择框并没有任何变化,用户无法准确的知道是否已经完成该操作
- 若用户选中全部后又取消某条数据,而后又选中,又取消·····而el-table的select事件无法判断出用户到底是选中还是取消,我又该如何告知服务器,用户随后有哪些操作呢?
尝试解决
-
对于问题1,经过一番思考后发现,我们可以在获取当前页数据的时候判断isAll字段,根据其值来遍历获取到的数据,用el-table中的toggleRowSelection函数来选中当前页的每一项,如
this.datalist = res.data; // datalist是表格绑定的数据,res.data是服务器响应的数据
if (this.isAll) {
this.datalist.forEach(item => {
this.$refs.tableRef.toggleRowSelection(item, true)
})
} -
对于问题二则是没想到好办法,于是乎陷入了僵局。
思考了几天后(恕我愚昧,想了几天才想出来),终于想到,干脆在点击选中全部的按钮后,让后端把所有筛选结果都发给我,将其存在selectedData数组中,同时用toggleRowSelection函数来选中当前的数据。之后每次触发select事件我就遍历selectedData,用indexOf判断select的这一行row是否在selectedData中,在则是取消选中,咱们从selectedData中删除该row;否则就是选中,push到selectedData中,在需要时再将selectedData传给后端
handleSelect(rows, row) {
if (this.selectedData.length !== 0) {
let isInner = false; // 用来判断是否是选中
let index = 0; // 用来记录某项数据在selectedData数组的索引,以便删除
this.selectedData.forEach(item => {
if (item.id === row.id) {
// 有和select项的id相同,说明已在selectedData中,是要取消该项的选择
isInner = true;
// 记录下来要取消的数据在selectedData中的索引
index = this.selectedData.indexOf(item);
}
});
// 在selectedData中,则删除该项
if (isInner) {
// 用splice方法将要取消的数据从selectedData中删除,并return结束该次select
return this.selectedData.splice(index, 1);
}
// 不在则push到selectedData中
this.selectedData.push(row);
} else {
// 如果selectedData为空,则直接push进去
this.selectedData.push(row);
}
} -
OK,搞定!跑起来!咦···怎么这里还有个选中当前页的选择框······
再遇问题
在点击选中当前页的选择框时,并不会触发select事件,而是触发的select-all事件或者selection-change事件。这两个事件默认接收一个rows(变化的数据数组)参数,在取消选中时rows被重置为空。这就又遇到了几个问题
- rows会被重置为空数组,我们可以用rows是否为空来判断是选中还是取消选中,但是我怎么知道是哪些数据的选中状态变化了呢?
- 若翻页后切换当前页的选中状态,由于我们用reserve-selection和row-key来保存了其他页面的选中状态,所以取消选中时rows并没有被重置为空,那我们该怎么判断当前页的全选状态呢?
- 若当前页有部分数据是选中状态(编辑时带入进来的),我切换当前页的全选按钮,又该如何处理呢?
解决问题
在无数次尝试后,我找到了解决办法
-
新增一个全局属性isSelectedAll,在触发select-all事件时取反,在翻页时重置为初始值false,我们再通过isSelectedAll来判断是否是全选
-
当前页的变化的数据始终都是当前的所有数据,所以我们可以直接用table绑定的datalist来遍历判断,哪些数据是已在selectedData中的
handleSelectAll (rows) {
this.isSelectedAll = !this.isSelectedAll;
if (this.isSelectedAll) {
// 为true说明是选中了全选,把本页的数据加进去
this.datalist.forEach(item => {
let isInner = false;
this.selectedData.forEach(sItem => {
if (sItem.id === item.id) {
isInner = true;
}
});
if (!isInner) {
this.selectedData.push(item);
}
});
} else {
// 为false说明是取消了全选,将本页的数据从selectedData中删除
this.datalist.forEach(item => {
this.selectedData.forEach(sItem => {
if (item.id === sItem.id) {
// 找到了对应的sItem,获得其索引,删除
return this.selectedData.splice(this.selectedData.indexOf(sItem), 1);
// index = this.selectedData.indexOf(sItem);
}
});
});
}
},
总结
要做到全选所有数据,主要有以下几点
- 从服务器拿到所有数据(这比较考验后端的算法了,如果拿得太慢,可能这里直接就超时报错了,当然,也可以只拿id过来,减小数据量),存在一个selectedData数组中
- 用reserve-selection和row-key属性在翻页时保存数据的选择状态
- 在select和select-all事件中分别判断选中的数据是否在selectedData,从而决定增删
- 在select-all事件中可以用一个全局变量来判断是选中还是取消,请一定记得在在翻页时重置