element ui 级联组件 vue级联组件_element ui 级联组件


写在前面

这一篇记录一下在使用vue库手写级联选择组件遇到的一个问题,当中涉及的知识点有组件通信,v-if与v-for的使用,props的使用,以及提供一些些组件设计的思路。

场景描述


element ui 级联组件 vue级联组件_嵌套_02


简单描述:当前层级["浙江"、"广东"、"黑龙江"],需要根据用户点击相关省份来显示下一层。

这里涉及的难点在于显示组件的设计,也就是结构的设计,

这里有两种思路:


// 嵌套型,第二层是嵌套在第一层里面的。
<div class="level1">
    <div class="level2">
</div>


// 独立型,第一层和第二层都独立,非嵌套
<div class="level"></div>
<div class="level2"></div>


直观上来选,肯定第一种。那现在就来试试。

解决过程


<template>
	<div class="level1"
         v-for="item1 in source"
         @click="selectLevel1 = item1">
        {{item1.name}}
		<div class="level2"
             v-for="item2 in for level2Items">
            {{item2.name}}
		</div>
	</div>
</template>
<script>
	export default{
		level2Items(){
            return selectLevel1.children?selectLevel1.children:[]
        }
    }
</script>


需要交代一下,此时的数据结构大概如下:


// source数据结构
[{name:"浙江",children:[{name:"杭州"},{name:"嘉兴"}],
 {name:"广东",children:[{name:"惠州"}]
]


然后发现显示,结果有问题:

当点击“浙江”时,["杭州","嘉兴"]这个整体会被打印两次。

会出现这样的结果也正常,因为在第二层循环时没有加判断条件


<div class="level1"
            v-for="item1 in source"
            @click="selectLevel1 = item1">
           {{item1.name}}
                <!--这里没有v-if判断语句去获取到底父元素的值为某值时才应该显示-->
		<div class="level2"
             v-for="item2 in for level2Items">
            {{item2.name}}
		</div>
	</div>


然后尴尬的是,level2Items这个计算属性并不能获取到父元素的标识。


[{name:"杭州"},{name:"嘉兴"}]


因此,我决定修改一下数据结构:


{parent_id:null,name:"浙江",id:1},
{parent_id:1,name:"杭州",id:2},
{parent_id:1,name:"嘉兴",id:3},
{parent_id:null,name:"广东",id:4},
{parent_id:4,name:"惠州",id:5}


再加上v-if判断条件


<template>
	<div class="level2"
          v-if="item2.parent_id === selectLevel1.id"
          v-for="item2 in level2Item"
          :key = item2.name
     >
         {{item2.name}}
     </div>
</template>


照样失败,还是和原来一样的结果。

查看一下vue文档写着这么一句:

v-ifv-for一起使用时, v-for具有比 v-if更高的优先级。请查阅列表渲染指南以获取详细信息

建议的方式是独立把v-if放在v-for之前


<template>
       <!--提示item2未被定义,获取不成功-->
       <div v-if="item2.parent_id === selectLevel1.id">
	<div class="level2"
          v-for="item2 in level2Item"
          :key = item2.name
     >
         {{item2.name}}
        </div>
       </div>
</template>


所以,方法一,嵌套组织方式宣告失败。

最终解决

采取了第二种组织方式:第一层和第二层分别独立。


<div class="level1">
     <div class="label"
           v-for="item1 in source"
           @click="level1Selected = item1"
           :key="item1.name">
            {{item1.name}}
       </div>
</div>
<div class="level2">
     <div class="label"
           v-for="item2 in level2Items"
           @click="level2Selected = item2"
           :key="item2.name">
             {{item2.name}}
</div>


因为不需要出现v-for和v-if混用还是抉择的问题。


compued:{
    level2Items(){
      // 通过level1Selected接受到第一层选择了什么,于是第二层也不需要判断了
      if(this.level1Selected){
        return this.level1Selected.children;
      }else{
        return []
      }
    },
}


唯一还需要注意的就是计算属性的初始化问题,在第一层未被点击时level2Items是没有值的状态。

总结

1.v-if和v-for同时使用时,v-for有优先级。若需要先判断,需将v-if往上推多一层。

2.设计上下级显示有通信关联的组件时,并且上下级有一对多关系的组件,应该考虑将父子元素分开,写成单独的形式,而不是嵌套。