自己写组件还是非常快乐的事情,所以决定从简单组件开始练手,也跟大家一起学习和成长。
工作中,经常遇到需要输入input,请求后台数据再渲染下拉列表的情况。类似于element-ui的select组件。但element组件必须输入item后再点击select-item,input的值才能录入。于是就有了想法,自己写一个可输入的下拉组件。
不啰嗦上干货~
组件结构分析
index.vue
<template><div class="dropdown-container" v-clickOutside="hide"> <!--slot 无法绑定事件 --> <div @click="handleClick"><slot></slot></div> <slot name="dropdown"></slot></div></template>首先看下模板, v-clickOutside 和 handleClick 实现下拉框组件的显示和隐藏。组件调用方不用关心如何控制dropdown的显示和隐藏。
<script>import Clickoutside from 'element-ui/src/utils/clickoutside';import Emitter from 'element-ui/src/mixins/emitter';export default { name: 'dropdown', componentName: 'dropdown', mixins: [Emitter], directives: { Clickoutside }, data() { return { visible: false } }, props: { }, methods: { hide() { this.visible = false }, handleMenuItemClick(command, instance) { this.visible = false; this.$emit('command', command, instance); }, handleClick(e) { this.visible = true } }, watch: { visible(val) { this.broadcast('dropdownMenu', 'visible', val); } }, mounted() { this.$on('menu-item-click', this.handleMenuItemClick); }}</script>引入clickOutSide、emiiter。不了解这两个插件的可以自行补充一下,这里不做详细阐述。 this.broadcast('dropdownMenu', 'visible', val);将父组件的visible属性,传给子组件dropdownMenu。
this.$on('menu-item-click', this.handleMenuItemClick);
handleMenuItemClick
// 当menu-item被点击后,将事件传给command<style lang="less">.dropdown-container { display: inline-block; ul { position: absolute; top: 50px; background-color: #283340; border: 1px solid #7e8b9d; max-height: 400px; padding: 5px; white-space: nowrap; li { cursor: pointer; a { max-width: 300px; line-height: 25px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; color: #fff; line-clamp: 2; &:hover { color: #fff; } } } }}</style>dropdown-menu.vue
这个部分逻辑很简单,直接上代码
<template> <transition name="el-zoom-in-top"> <ul v-show="showPopper"> <slot></slot> </ul> </transition></template>
<script> export default { name: 'dropdownMenu', componentName: 'dropdownMenu', props: { visibleArrow: { type: Boolean, default: true }, arrowOffset: { type: Number, default: 0 } }, data() { return { showPopper: false }; }, created() { this.$on('visible', val => { this.showPopper = val; }); } }</script>
设置transition特效,showPopper控制dropdown-menu的显示和隐藏。index.vue 广播过来的visible事件,来改变showPopper的值。
dropdown-item.vue
最巧妙的设计在于command和menu-item-click之间的关联。避免我们在外部调用该组件的时候,还要手动添加点击事件。
<template> <li class="dropdown-menu__item" @click="handleClick" > <slot></slot> </li></template><script> import Emitter from 'element-ui/src/mixins/emitter'; export default { name: 'dropdownItem', mixins: [Emitter], props: { command: {} }, methods: { handleClick(e) { this.dispatch('dropdown', 'menu-item-click', [this.command, this]); } } };</script>用到Emitter,将menu-click-click事件广播给dropdown,然后通过command暴露给外部组件。props:command来存储点击选中的值。
是不是这个组件非常简单就实现了呢?
接下来看我们怎么调用
template
<dropdown @command="handleCommand"> <el-input v-model="value" @input="handleInput" laceholder="请输入具体地址" /> <dropdown-menu slot="dropdown"> <dropdown-item :command="item" v-for="(item,index) in list" :key="index" @click="syncInput(item.value)">{{item.address}}</dropdown-item> <dropdown-item v-if="areaList.length === 0">暂无数据</dropdown-item> </dropdown-menu> </dropdown>script
<script>
export default {
data(){
return {
value:"",
list:[{value:'a',key:1}]
}
},
methods:{
handleCommand(command) { this.value = command.value }, handleInput(){
// 处理业务逻辑
}
}
}
</script>是不是so easy!
如果想要源码请关注我的git