使用v-model实现select下拉框组件

一、组件使用场景及需求分析

  • 表单多个固定单值的情况,我们不用再去input框输入值,直接在固定的值里面去选择

  • 选择以后父组件绑定的值对应改变,使得不需要发送表单前再进行赋值

  • 选择前后,列表都是不可见的

二、开始我们的codeing

  • 首先我们需要的是一个有所有单值选项的list展示

  • 然后是一个展示当前选择的文字框

像这样的:

01.png

这一步我们只需要父组件传递单值代码,然后当前选中的一个值,没有的话就默认为空。

vue:

  • {{item.name}}

JS:

 props: {
   list: {
   type: Array,
   required: true,
   },
   selected: Object,
 },
 data() {
   return {
     scoped: {
     // 当前选中的
     selected: this.selected,
     },
   };
 },

CSS:

.fd-select-box {
   position: relative;
   width: 200px;
   padding-right: 40px;
   padding-left: 10px;
   height: 36px;
   margin: 30px auto;
   line-height: 36px;
   border: 1px solid #41b883;
   border-radius: 4px;
   color: #000;
   font-size: 14px;
   text-align: left;
   cursor: pointer;
   box-sizing: border-box;
​
   .fd-arrow {
     position: absolute;
     top: 0;
     right: 0;
     font-size: 30px;
     transition: all 200ms;
​
       &.fd-down {
         transform: rotate(180deg);
         }
     }
​
 .fd-select-list {
   position: absolute;
   width: 100%;
   max-height: 200px;
   overflow: auto;
   list-style: none;
   top: 36px;
   left: 0;
   background: #fff;
   box-shadow: 0 0 5px rgba(0,0,0,0.2);
   z-index: 9;
​
     li {
       padding-left: 12px;
       line-height: 30px;
       cursor: pointer;
​
     &:hover {
       background: rgba(65, 191, 138, 0.2);
     }
​
     &.active {
       background: rgba(65, 191, 138, 0.9);
       color: #fff;
     }
 }
 }
 }

这样,我们已经把外层框架搭建好了。

接下来,解决子组件改变,父组件对应的值发生改变。

一般情况我们会想到子组件把当前选中的值通过this.$emit传给父组件,然后父组件再在对应方法里面给对应的值赋值。今天我们用另外一种方法来解决这个问题,那就是v-model,相信我,用了它你会爱上它。

好了,我们看看官方文档怎么说的:

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

 model: {
   prop: 'checked',
   event: 'change'
 },
 props: {
   checked: Boolean
 },
 template: `
   
 `
})

https://cn.vuejs.org/v2/guide/components-custom-events.html

我的理解就是提供了v-model;在自定义组件上model里的prop里的字段的值会直接赋给props里面对应字段,像之前我们给checked传值是在父组件上通过:checked='false'这样一种形式。现在我们可以使用v-model='false'。来看具体在select框里面的表现吧。

export default {
   name: 'fdSselect',
   model: {
   prop: 'selected',
   event: 'changeValue',
 },
 props: {
   list: {
     type: Array,
     required: true,
   },
   selected: Object,
 },
 data() {
   return {
     scoped: {
     // 是否展示下面的列表
     showFlag: false,
     // 当前选中的
     selected: this.selected,
     },
     };
 },
 methods: {
   // 值改变后传给父组件,因为组件定义了model,所以父组件相当于执行了绑定的model值=emit出去的值
   changeValue(item) {
     this.scoped.selected = item;
     this.scoped.showFlag = false;
     this.$emit('changeValue', this.scoped.selected);
     },
   },
};

父组件调用:

 

上面的event是我们要emit出去的事件名。这一步相当于在父组件执行了父组件的this.selected等于子组件的this.scoped.selected;所以其实你用组件的时候v-model="value"其实就是:value="value" @change="(val) => {value = val}"

现在看看我们实现的效果:

02.gif

前两个需求已经实现了,最后一个需求是在交互上的优化。

首先他要一开始的时候不展示,我们给一个控制下拉框显隐的变量。showFlag默认值为false;点击输入框时展开下拉列表。然后选中选项后隐藏下拉列表。

注意我们的页面结构,下拉列表是输入框的子元素,所以点击下拉列表元素的时候会涉及到事件冒泡,这个时候我们使用.stop修饰符来组织时间冒泡导致下拉列表一直不能隐藏。

vue:

 

  • {{item.name}}

JS:

 // 值改变后传给父组件,因为组件定义了model,所以父组件相当于执行了绑定的model值=emit出去的值
 changeValue(item) {
   this.scoped.selected = item;
   this.scoped.showFlag = false;
   this.$emit('changeValue', this.scoped.selected);
 },
 // 改变下拉选项的显隐
 changeShow() {
   this.scoped.showFlag = !this.scoped.showFlag;
 },

继续优化,我们现在实现了组件列表的显隐,但是只有操作当前组件时可以控制。那么我们点击其他地方的时候,其实也是希望组件列表可以隐藏起来的。

实现这个的思路:绑定一个点击事件在页面上,只要点击的元素不是当前组件,那么我们就可以隐藏当前组件的列表。这里我用到了自定义指令,具体实现如下:

clickOutside: {
   bind(el, binding) {
   function clickHandler(e) {
   // 这里判断点击的元素是否是本身,是本身,则返回
   if (el.contains(e.target)) {
     return false;
   }
   // 判断指令中是否绑定了函数
   if (binding.expression) {
   // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
     binding.value(e);
   }
   return true;
 }
 // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
   el.vueClickOutside = clickHandler;
   document.addEventListener('click', clickHandler);
 },
 unbind(el) {
   // 解除事件监听
   document.removeEventListener('click', el.vueClickOutside);
   delete el.vueClickOutside;
   },
 },

最后实现效果如图:

03.gif

后期待优化:实现可搜索的下拉框-->实现可以远程搜索的下拉框

以上为个人编写,希望能对大家的项目有所帮助,如有不当以及有更好的方法欢迎交流。

项目地址: https://github.com/jasminezx/select.git

你可能感兴趣的:(使用v-model实现select下拉框组件)