iview+treeSelect组件,我是如何一步步手动实现全选功能的

如果我掏出下图,阁下除了私信我加入学习群,还能如何应对?

编辑

在这里插入图片描述

@TOC


前言

因为种种历史原因,我们一直选择的vue前端框架是iview,而非element,在老版的iview中,treeselect(树选择组件)一直都是半成品,后来团队买了iview pro版,树选择组件虽然能够使用,但功能仍显单一,缺少全选功能。

现在项目要求实现全选,就只能自己动手了。

心情烦躁者,会觉得一多半文章在讲废话,请直接下拉到最后查看实现方式。


一、历史问题

研究早期iview版本的treeselect源码,我们可以将它和数据有关系的部分分为两类: 1. 树形图数据 2. select选项数据

select选项数据其实又可以分为两部分: 3. 下拉框中选项部分; 4. input中的tag标签显示部分。(看着像input,其实是渲染成div了,为了便于理解,就这么称呼了)

如图所示:

编辑

在这里插入图片描述

这几部分数据大致的逻辑如下: 1.渲染树形图数据,在下拉框中形成树形图。 2.点击树形图的数据,会触发select的方法,选中点击的数据 3.select方法被触发后,会触发input方法,修改tag标签的显示。(这个是真的input方法,形如:vm.$emit('input',xxxxx))

逻辑很简单,不简单的是,当数据增加、删除、改变时,这三个数据逻辑之间互相监听,来回改变,最终为了实现三项数据的联动,导致数据变化监听十分复杂。

这就导致我们如果要实现全选功能,最好就不要再去通过监听select事件来实现。

二、通过监听select事件实现全选不靠谱!!!

iview pro版本是采用函数组件的方式,查看它源码后,会发现虽然代码清爽了许多,但是三者之间还是联动。

所以我不想再采用监听select的方式。原因有二:

  1. 监听select,触发某个条件后再手动为select赋值,会继续触发监听,形成死循环。
  2. 上面的问题可以解决,但代码过多,并无必要,而且需要修改源码,不利于后续框架升级。

切记:研究新旧版select代码的过程绝对不舒服,希望大家有所了解就行,不要好奇去看它代码,会被各种监听搞吐的!!!

三、 通过外部事件控制树选择组件

既然不能监听内部的select事件,那我通过和树组件并不想关的一个事件,调用select提供的方法,是不是就能避免修改源码,并且不会形成死循环。

尝试后,发现是可以的。具体操作如下: 1. 创建一个普通的树组件,其中树形图数据为treeData,组件的v-model绑定的数据为:argStrList,代码如下

ruby 复制代码
<TreeSelect ref="treeSelect" :max-tag-count="3" multiple v-model="argStrList" :data="treeData"/>
  1. 在组件毫不相关的地方,创建一个button,点击事件中修改argStrList,并做个延迟,方便我们有时间打开下拉框查看变化。代码如下:
javascript 复制代码
setTimeout(()=>{
              this.argStrList=allValueArr
            },3000)

allValueArr是我创建的一个value值数组,具体是什么,根据各自项目情况而定。

  1. 打开下拉框后,发现过了两三秒,下拉框中对应的选项被选择,input中的tag也相应维护,说明外部事件控制select来触发创建,并不会产生额外的副作用。

但是这种方式也实在太不优雅了,所以就想到在下拉框里渲染元素和事件,但是并不和select本身的元素和事件关联。treeselect组件并没有直接提供在树形图里面添加其它元素的方式。但树选择组件是基于tree组件的,tree组件提供了render函数自定义元素内容。

所以我们要做的就是在tree树形图中,创建一个能被我们自由控制的元素和事件。

四、render函数创建组件

4.1 不得不说的h函数

不得不说,h函数很多场景很好用,但在反人类的道路上一去不返,我并不支持这种写法,所以只列代码,不讲解:

less 复制代码
render: (h, { root, node, data }) => {
          return h('span', {
            style: {
              display: 'inline-block',
              width: '100%'
            }
          }, [
            h('span', [
              h('span', data.title)
            ]),
            h('span', {
              style: {
                display: 'inline-block',
                float: 'right',
                marginRight: '32px'
              }
            }, [
              h('Checkbox', {
                style: {
                },
                on: {
                  'on-change': () => {
                    this.argStrList.push('1400344119453511682')
                  }
                }
              }),
              h('span','全选')
            ])
          ]);
        },

4.2 如果条件允许,请使用jsx

Babel版本3.4.0开始已经支持jsx,相当老的版本了,如果项目里插件版本过低,请升级。

树形图root节点的全部代码如下:

javascript 复制代码
let root = {
        id: '0',
        name: '组织架构树',
        type: 0,
        render:(h,{root,node,data})=>{
          const key='value' || "id"
          const allValueArr=_.map(_.map(root,'node'),key)
          const checkChange=(e)=>{
            if(e){
              this.argStrList=allValueArr
            }else{
              this.argStrList=[]
            }
          }
          return(
            <div>
              <Checkbox vOn:on-change={(e)=>checkChange(e)}>{data.title}</Checkbox>
            </div>
          )
        }
      }

这段代码主要就是在root节点上渲染了一个checkbox,并添加事件。如图:

编辑

在这里插入图片描述

注意点: 1. jsx语法请查看github官网文档; 2. _.map是loadash提供的api,作用是把所有树形节点的value取出来组成数组allValueArr;


总结

  1. treeSelect组件内部各种互相监听太复杂了,很容易动一处爆两处bug,所以慎动。
  2. 内部监听事件解决不了,就做个外部事件。然后用黑科技,把外部事件做到tree里,看起来好像是本身的一部分一样。这样做出来的全选功能,就只在treedata数据里增加一个属性,对项目代码和treeselect组件代码影响都是最小的。
  3. treeselect这种实现方式应该引以为戒,太难维护了。我虽然没有实操,但是这种很多类数据之间有关联的场景,应该尽可能用单例模式,创建个类或者自执行的闭包,相当于模拟一个三两个组件共享的小型状态管理工具,把数据维护到内存。尾大不掉还是确实没办法这样去实现,不得而知。
  4. 后续如果想做级联选择,就是改变on-change事件里的算法即可
相关推荐
前端李易安2 小时前
Web常见的攻击方式及防御方法
前端
PythonFun2 小时前
Python技巧:如何避免数据输入类型错误
前端·python
hakesashou2 小时前
python交互式命令时如何清除
java·前端·python
天涯学馆2 小时前
Next.js与NextAuth:身份验证实践
前端·javascript·next.js
HEX9CF2 小时前
【CTF Web】Pikachu xss之href输出 Writeup(GET请求+反射型XSS+javascript:伪协议绕过)
开发语言·前端·javascript·安全·网络安全·ecmascript·xss
ConardLi2 小时前
Chrome:新的滚动捕捉事件助你实现更丝滑的动画效果!
前端·javascript·浏览器
ConardLi3 小时前
安全赋值运算符,新的 JavaScript 提案让你告别 trycatch !
前端·javascript
凌云行者3 小时前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
华农第一蒟蒻3 小时前
Java中JWT(JSON Web Token)的运用
java·前端·spring boot·json·token
积水成江3 小时前
关于Generator,async 和 await的介绍
前端·javascript·vue.js