wencaizhang

记 Ant Design Vue 中表格组件限制选择数量引起的一个 BUG

Published onMay 09, 2024
-Views
3Minutes Read

背景

有一个 Vue (vue2.6 版本)项目使用了 Ant Design Vue (1.6.5版本) 作为 UI 库,其中组件表格 Table - Ant Design Vue 提供了选择行的功能。
核心代码如下:
其中:
  • 属性接收
    用于回显当前已选中的行,每当行选择发生变化时候触发
  • 中有一个数组
    保存所有选中行的
  • 方法
    用来监听行的选中和取消选中的变化,每次变化形参
    是变化之后由
    组件传递过来的当前选中行的
    数组,我们拿到之后就存到
    上。
因此这样就形成了一个闭环。

需求

现在由于业务需要, 有一个表格要求最多只能选择两行, 而 表格 Table - Ant Design Vue 只提供选择功能, 并没有限制个数的功能, 因此这个需求只能自己想办法实现。
针对要求最多选中两行的需求,我的处理方案是监听 change 事件, 判断当前已经选中的行数, 如果大于或等于 2 则不更新, 小于 2 才更新选中的值, 新的
函数代码如下:

出现 Bug

上面的代码看起来好像没什么问题,逻辑也很简单,但就是出现了很奇怪的现象。
首先选中两行之后再去选中第三个行, 由于
中有判断和拦截, 因此不能选中成功, 这是符合预期的正确现象。
接下来我把之前选中的其中一行取消选择,然后再来选择刚才被取消的这一行, 奇怪的现象(Bug)出现了:点击勾选,网页却没有变化,再点击一次才能看到勾选成功。
经过反复点击试验, 并不是每一行都会出现这个 Bug,会出现这个 Bug 的行都有这样的特点:曾经勾选的时候被
阻止选中。
也就是说只要被「阻止选中」过,都需要点击两次才能完成勾选,而其他行就没有这个问题。

分析 Bug 找出原因

难道是
有啥问题?这是我的第一反应,我决定使用
大法。
然而看不出什么问题。首先参数没问题,
接收到的参数和眼睛观察到的是一致的,也就是说「阻止选中」过的行第一次点击勾选的时候对应 id 没有出现在
参数中,因此就无法回显给
那么为什么命名点击勾选,却没有拿到它的 id 呢?🤔 很奇怪!
我不断地念叨“点击两次才能选中...点击两次才能选中...”,突然灵机一现:会不会是 checkbox 本来是选中状态,所以点击两次又回到选中状态了
好像只有这样才能解释通为什么需要点击两次,也能解释通其他行不需要点击两次,但是如果 checkbox 是选中状态,为什么页面并没有显示为选中状态呢?
突然想起我之前封装 checkbox 的经历:为了能灵活控制 checkbox/radio 等表单元素的样式,通常会把原生 input 标签弄成透明的(不是隐藏,因为需要用户能点击),然后利用 label 搭配 css 选择器(
)来实现选中和未选中的样式。
如果按照这个逻辑,我看到
并不是真正的
而是一个障眼法,而我在
中的代码并不能真正阻止
被选中,我只是阻止了被选中的样式出现,在我看不到的地方,真正的
实际上已经处于选中状态了。
这样一来,对
的两次点击的作用分别是取消勾选和勾选,但是由于在这两次点击的时候,当前已选中的行少于 2 故而没有触发
的拦截,所以勾选样式就能正常的回显到页面上了。
通过审查元素去验证果然如此:
可以看到真正的
是透明的,并且使用定位和
来保证即使看不到仍然能点击它。
的兄弟节点来负责样式。
因此这个问题的原因在于我简单地以为通过
阻止
执行,就可以阻止选中某一行了。
实际上这样做还不够,这样只在样式上达到效果了,样式上是根据传入的
来决定是否北选中,但是原生标签
的状态没有被阻止,实际上已经变成选中状态,因此就导致样式和状态的不统一。

修复 Bug

所以解决办法就是拦截选中的时候,深入到 DOM 级别来修改
的选中状态。
由于
事件无法拿到事件对象
,所以通过文档找到了另外一个事件处理函数
,在
中能够拿到原生的
对象,通过
对象就可以控制
的状态了。
参数描述类型
onChange选中项发生变化时的回调
onSelect用户手动选择/取消选择某列的回调
最终代码如下:

总结

  1. 使用 Vue 框架我们可以减少 DOM 操作,但有时候还是需要和 DOM 打交道。
  2. 不要想当然,本文 Bug 的出现就是我想当然得以为阻止
    的赋值就能阻止选中状态,但并不是这样
  3. 遇到问题需要能够深入分析问题,不能附在表面。
Tags:
#Antdv
#Ant Design Vue
#Vue
#Bug