100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 组件Element-UI 2实现树形下拉选择框--详解(含代码示例)

组件Element-UI 2实现树形下拉选择框--详解(含代码示例)

时间:2020-12-01 12:53:11

相关推荐

组件Element-UI 2实现树形下拉选择框--详解(含代码示例)

笔者由于项目原因需要用element-ui 2实现此效果(如下所示)。本文根据Element-UI 2的el-select和el-tree实现树形下拉选择框的效果,适用于想实现效果但项目组件版本未升级的情形,小白也能看懂!(源码在最后-->)

本文主要参考了下面这位大佬的代码,并在其基础上提出了自己的一些见解:

elementui下拉树形结构【完美实现】by 来干了这碗代码

下面笔者将从组件开始介绍。对组件了解的朋友可以点击标题跳转至代码部分。

1.组件介绍

顾名思义,树形下拉选择框需要用到Tree控件和Select选择器。

1.1Select选择器

官方文档:组件 | Select选择器

Select选择器实现的是下拉的效果,由el-select组件包裹着el-option。el-select的主要熟悉为v-model,el-option的主要属性为v-for和:'value'。

<el-select v-model="value"><el-optionv-for="item in cities":value="item.value"></el-option></el-select>

select的v-model属性实现选择器所选节点的显示以及动态绑定的效果。

option的:value属性绑定的是选项的值,v-for则表示该选项的值从数组中循环(通常数据是以包裹对象的数组的形式传入前端)。

1.2Tree树形控件

官方文档:组件 | Tree树形控件

el-tree实现的是树形菜单的效果,其最主要的三个属性分别是data,props属性和node-click事件

<el-tree:data="data":props="defaultProps"@node-click="handleNodeClick"></el-tree>

其中,data负责数据的绑定(数据通常是树形数组的形式)。

props负责数据的传递(引用网上的一句解释:“父组件通过 props 向下传递数据给子组件;子组件通过 events 给父组件发送消息”)。

而node-click则负责点击节点时的操作(就是click事件套了层皮而已)。

好了,现在你已经了解组件的基本用法了,试着实现树形下拉框的效果吧!

2.代码详解

实现目标效果的方法是select和tree的嵌套,细分下来可有两种方式,分别是option与tree平级(option 1)和option嵌套tree(option 2)。但这两种方式的基本思想是相通的。

<!-- option 1 --><el-select><el-option></el-option><el-tree/></el-select><!-- option 2 --><el-select><el-option><el-tree/></el-option></el-select>

2.1 option 1

此方法也正是参考文章的博主所采用的方法。

(1)数据处理

示例数据为树形数组。(一般而来说需要自己将后端返回的数据转换为树形数组,示例为了方便省略了这一步,后面会介绍平面数组与树形数组相互转换的方法

cityData: [{id: 1,label: '重庆',children: [{id: 2,label: '渝北区'}]}, {...},{...}]

在script模块data()中定义数据。

(2)模板构造

a. el-selelct

绑定v-model="selectValue",selectValue将显示所选节点的值。还需要绑定响应式属性selectTree来实现用户点击不同节点的响应式效果。

b. el-option

el-option是下拉框的选项,每项的属性由v-for循环数组(即方法optionData()的返回值,其中返回值是平面数组)绑定。选项的名称为数组元素的label,值为数组元素的value。

c. el-tree

作为目标效果的主体,el-tree需要绑定cityData数据,与之配套的treeProps属性、selectTree响应式属性和handleNodeClick事件。至于:expand-on-click-node="expandOnClickNode",则表示是否只有点击箭头列表才收缩;default-expand-all表示默认展开所有节点。

效果如下图所示。因为采用的是option与tree平级的方法,所以上半部分是下拉框,下半部分是树形目录。

这个时候只需要给el-option添加display:none样式即可实现目标效果。

(3)操作绑定

a. 树形数组转平面数组

在2.1>(2)>b的el-option中,需要用v-for给该组件绑定数据,而我们的数据是树形的,因此需要将树形数组转换为平面数组。参考文章采用的方法仅仅适用于4级数组,也就是最多只能有4级菜单,而且代码冗杂、有很多重复的地方。于是笔者采用迭代的方法实现此目的。

optionData方法的内容很简单,就是迭代加循环。方法有两个参数,一是源数组,二是存放结果的空数组。对源数组进行forEach遍历循环:将当前元素push进结果数组。其中结果数组的label是源数组的label,value是源数组的id。然后再对当前元素进行判断:如果其有children节点,则再次执行optionData方法,而参数则是该元素的children节点和第一次迭代的result。这样直到最后一个元素判断后,将返回result。

至于为何要用JSON.parse(JSON.stringify())的方式,是为了防止出现__ob__:observe的而造成无限循环的情况。通过数据深拷贝后,可以去掉数组的__ob__:observe属性。具体可以参考下面这篇文章,里面提到了更多解决方法。

“__ob__: Observer是 Vue 对数据监控添加的属性。当数据中包含这个属性时,数据是不可枚举、不可遍历的。这里深拷贝的数据应该是我们最终用于赋值的数据,而不是接收到接口的返回值。

参考文章:Vue 去掉数组中的 __ob__ : Observer

最终的结果如下图所示:

b. 绑定节点事件

在2.1>(2)>c中绑定了事件handleNodeClick。点击节点时,el-select的selectValue属性为当前节点的label值,同时el-tree也会响应式失去焦点。

我们可以通过console.log检验是否正确。从下面的输出来看是无误的。

小结一下:

1.由于el-select必须与el-option配套使用,所以可以通过设置display:none的样式隐藏多余的选项;

2.为el-option绑定数据时,因为要用到v-for,因此需要将树形数组转换为平面数组。而为了降低代码的冗余度,故采用迭代的思想来实现效果;

3.vue会为数据增添observe的属性来监听变化,使用深拷贝的方式可以有效去掉,但要注意需要对使用的最终数据进行拷贝而不是返回值。

2.2 option 2

在option 1中,我们对el-option添加了display:none样式来隐藏不必要的选项、还将数组进行了转换,是否有些多此一举了?能否果断一点直接把el-tree包裹在el-option里?因此,笔者尝试用option 2实现此构思。

(1)数据处理

因为不给el-option绑定数据,因此不必对数据做过多的处理。

(2)模板构造

el-select和el-tree的构造与option 1并无大异,主要将el-option的属性全去掉了。

直接Ctrl+s,效果如下所示,然后报错了。不急,我们一个一个解决。

a. 解决样式的问题

首先是下拉框的样式错误。在树形菜单左右两侧都有灰色的背景,并且菜单也没撑满下拉框。我们只需要添加样式让树形菜单撑满即能解决此问题。

效果如下所示,我们成功解决了!

b. 解决控制台的报错

由报错信息可知,我们好像缺少一个value的属性。这是因为在使用element-ui时,el-select未绑定v-model或el-option未进行value赋值。显而易见是后者造成的原因。因此只需要添加一个小小的value:' ',即可解决报错。

大功告成!吧唧吧唧吧唧。

小结两下:

1. 也可以el-tree嵌套在el-option,这样有两个好处:一是更易于理解,毕竟是将下拉的树形菜单;二是减少没有必要的代码,包括数组的转换,数据的绑定;

2. 造成Missing required prop: “value”的原因通常由是el-select或el-tree造成。

3. Element-UI 3新增组件实现

是的,element-ui 3中新增了el-tree-select组件来实现树形下拉框。

官方文档:组件 | TreeSelect 树形选择

因为此组件是由el-tree和el-select结合而来,并未修改原有属性,故组件的属性和事件也是两者结合。

4. 源码分享

<template><div class="app-container"><el-select class="main-select-tree" ref="selectTree" v-model="selectValue" style="width: 300px;"name='option 1'><!-- otion 1 --><el-option v-for="item in optionData(cityData)" :label="item.label" :value="item.value"style="display: none;"/><el-tree class="main-select-el-tree" ref="selectelTree" :data="cityData" :props='treeProps'highlight-current @node-click="handleNodeClick":expand-on-click-node="expandOnClickNode" default-expand-all /><!-- option 2 --><!-- <el-option style="height: 100%; padding: 0;" value=""><el-treeclass="main-select-el-tree"ref="selectelTree" :data="cityData":props='treeProps'@node-click="handleNodeClick":expand-on-click-node="expandOnClickNode"highlight-currentdefault-expand-allstyle="font-weight: normal;"/></el-option> --></el-select></div></template><script>export default {data() {return {selectValue: '',expandOnClickNode: true,options: [],treeProps: {children: 'children',label: 'label'},cityData: [{id: 1,label: '重庆',children: [{id: 2,label: '渝北区'}]}, {id: 3,label: '北京',children: [{ id: 4, label: '海淀区' },{ id: 5, label: '朝阳区' }]}, {id: 6,label: '四川',children: [{id: 7,label: '成都',children: [{ id: '8', label: '成华区' }]}]}]}},methods: {/*** 树形转平面的迭代方法* option 1的el-option需要此方法绑定数据*/optionData(array, result=[]) {array.forEach(item => {result.push({label:item.label,value:item.id})if (item.children && item.children.length !== 0) { this.optionData(item.children, result)} })return JSON.parse(JSON.stringify(result))},// 点击节点的响应handleNodeClick(node) {this.selectValue = node.label;this.$refs.selectTree.blur();console.log(node.label);}}}</script><style>.main-select-el-tree .el-tree-node .is-current>.el-tree-node__content {font-weight: bold;color: #409eff;}.main-select-el-tree .el-tree-node.is-current>.el-tree-node__content {font-weight: bold;color: #409eff;}</style>

因为笔者刚开始接触前端,如果文章中有错误恳请指正。如果这篇文章帮到你,不妨点个小心心支持一下笔者,阿里嘎多~

.6.1 更新

关于扁平数组与数据数组转换的方法已更新,感兴趣的朋友可以看我另一篇文章💖

扁平数组与树形数组的相互转换(详例 & 代码)--HelloWord精通

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。