組件封裝之輸入框下拉列表
作者:Tokiya
前言
項目開發(fā)的時候剛好遇到一個需求,需要在輸入框輸入名字的時候,彈出相應的人員列表提供選擇,然后將數(shù)據(jù)賦值給輸入框。項目是使用iview組件的,一開始想著在自定義iview的下拉選擇,后來發(fā)現(xiàn)效果并不理想。為了實現(xiàn)功能,就在iview輸入框的基礎上進行了組件封裝,下面就來講下組件封裝的過程。
思路:
對于組件封裝,首先需要確定功能,組件的整體結(jié)構,后面再去處理組件的數(shù)據(jù)交互邏輯。
過程:
組件的結(jié)構以及樣式:
話不多說,先把組件基本的結(jié)構樣式貼出來。
組件布局:
<template>
<div class="selectInput">
<div class="input-box">
<Input type="text" />
</div>
<div class="input-dropdown">
<div class="dropdown-title">
您可能需要查找
</div>
<div class="dropdown-content">
<ul class="content-list">
<li class="list-item">
<span class="item-avatar">李</span>
<span class="item-name">李四</span>
</li>
</ul>
<!-- 沒有數(shù)據(jù)時的提示信息 -->
<div class="content-msg">
暫無數(shù)據(jù)
</div>
</div>
</div>
</div>
</template>既然是輸入下拉,就需要一個輸入框以及一個下拉列表。
樣式:
.selectInput {
position: relative;
.input-dropdown {
min-width: 200px;
position: absolute;
top: 35px;
left: 0;
border: 1px solid #cfcfcf;
border-radius: 5px;
.dropdown-title {
padding: 5px 10px;
color: #e1e1e1;
background-color: #f8f8f8;
}
.dropdown-content {
.content-list {
margin: 0;
padding: 0;
.list-item {
margin: 0;
padding: 5px 10px ;
list-style: none;
&:hover {
cursor: pointer;
background-color: #f7f5f5;
}
.item-avatar {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
color: #ffffff;
background-color: #b6c4de;
border-radius: 50%;
text-align: center;
margin-right: 10px;
}
.item-name {
display: inline-block;
color: #666666;
}
}
}
.content-msg {
padding: 10px;
color: #cccccc;
}
}
}
}
寫完布局以及樣式之后的效果:

組件的功能邏輯:
1. 確定父子組件數(shù)據(jù)傳遞的props
props: {
// 父組件傳遞的輸入框初始值
value: {
type: String,
default: ''
},
// 下拉列表的標題
dropdownTitle: {
type: String,
default: '您可能要找'
},
// 下拉列表搜索數(shù)據(jù)為空時的提示
dropdownMsg: {
type: String,
default: '數(shù)據(jù)為空!'
},
// 下拉列表的初始數(shù)組
dropdownList: {
type: Array,
default: () => []
},
// 輸入框的提示
placeholder: {
type: String,
default: '請輸入名字'
},
}
2. 定義組件的data
data() {
return {
// 控制下拉列表顯示
dropdownShow: false,
// 控制下拉列表數(shù)據(jù)為空提示顯示
dropdownMsgShow: false,
// 輸入框值
inputValue: '',
// 搜索后的下拉列表,用于渲染下拉
searchDataList: []
}
}
3. 下拉列表的搜索邏輯
<template>
<div class="selectInput">
<div class="input-box">
<Input :placeholder="placeholder" v-model="inputValue" @input.native="handleInput" type="text" />
</div>
<div v-show="dropdownShow" class="input-dropdown">
<div class="dropdown-title">
{{ dropdownTitle }}
</div>
<div class="dropdown-content">
<ul class="content-list">
<li v-for="(item, index) in searchDataList" :key="index" @click="handleChoose(item.name)" class="list-item">
<span class="item-avatar">{{ item.avatar }}</span>
<span class="item-name">{{ item.name }}</span>
</li>
</ul>
<!-- 沒有數(shù)據(jù)時的提示信息 -->
<div v-show="dropdownMsgShow" class="content-msg">
{{ dropdownMsg }}
</div>
</div>
</div>
</div>
</template>
通過dropdownShow去控制下拉列表的顯示隱藏,給輸入框綁定一個inputValue值和一個input事件。
// 輸入框輸入處理函數(shù)
handleInput: debounce(function () {
let _this = this;
_this.searchDataList = [];
if (_this.inputValue === '') {
_this.dropdownShow = false;
} else {
_this.dropdownList.map(v => {
if (v.name.indexOf(_this.inputValue) >= 0) {
_this.searchDataList.push(v);
}
});
_this.searchDataList.length > 0 ? _this.dropdownMsgShow = false : _this.dropdownMsgShow = true;
_this.dropdownShow = true;
}
})
handleInput我做了防抖處理,在這個函數(shù)里面通過監(jiān)聽輸入框輸入事件,判斷輸入框的inputValue是否為空,若為空則直接隱藏下拉列表。不為空則循環(huán)迭代從父組件傳遞過來的dropdownList,并將符合條件的item存進searchDataList,然后在組件中通過v-for渲染出數(shù)據(jù)。
最后通過判斷searchDataList的長度給dropdownMsgShow賦值,來控制搜索數(shù)據(jù)的提示信息。
4. 搜索后的點擊選擇處理
給下拉列表的每一項li綁定一個點擊事件handleChoose。
// 下拉選擇處理函數(shù)
handleChoose: function (val) {
let _this = this;
_this.inputValue = val;
_this.dropdownShow = false;
}
點擊之后對輸入框進行賦值,并隱藏下拉列表。
5. 給組件添加一個clickoutside指令
自定義clickoutside指令,當點擊組件外的區(qū)域時隱藏下拉列表。
directives: {
// 自定義指令用于處理點擊組件區(qū)域之外的click事件
clickoutside: {
bind(el, binding, vnode) {
function documentHandler(e) {
if (el.contains(e.target)) {
return false;
}
if (binding.expression) {
binding.value(e);
}
}
el.__vueClickOutSize__ = documentHandler;
document.addEventListener("click", documentHandler);
},
unbind(el, binding) {
document.removeEventListener("click", el.__vueClickOutSize__);
delete el.__vueClickOutSize__;
},
},
},
給指令綁定一個關閉事件handleClose。
// 下拉列表隱藏處理函數(shù)
handleClose: function() {
let _this = this;
if (_this.dropdownShow) {
if (_this.searchDataList.length === 1) {
_this.inputValue = _this.searchDataList[0].name;
} else {
_this.inputValue = '';
}
}
_this.dropdownShow = false;
},
在這個函數(shù)里我做了一個處理,當點擊的時候,搜索列表有數(shù)據(jù)時,會默認選中第一個,否則清空輸入框。
關于函數(shù)防抖以及clickoutside,網(wǎng)上有大佬發(fā)了一些關于這些的文章,我在這里就不進行贅述了。
至此,組件封裝完成,組件的大體思路是這樣子,具體的邏輯處理可以根據(jù)實際情況進行相應的調(diào)整。
最后附上整個組件的代碼:
調(diào)用代碼:
<template>
<div id="blog">
<select-input :dropdownList="personnelList"></select-input>
</div>
</template>
<script>
/* 引入輸入下拉組件 */
import selectInput from './component/selectInput';
export default {
name: 'blog',
components: {
selectInput
},
data() {
return {
personnelList: [
{
avatar: '張',
name: '張三'
},
{
avatar: '李',
name: '李四'
},
{
avatar: '王',
name: '王五'
},
{
avatar: '趙',
name: '趙六'
},
{
avatar: '李',
name: '李師師'
}
]
}
},
methods: {
},
created() {
},
mounted() {
}
}
</script>
<style lang="less" scoped>
#blog {
padding: 50px;
width: 500px;
margin: 100px auto 0;
background: #ffffff;
height: 500px;
}
</style>
組件代碼:
<template>
<div v-clickoutside="handleClose" class="selectInput">
<div class="input-box">
<Input :placeholder="placeholder" v-model="inputValue" @input.native="handleInput" type="text" />
</div>
<div v-show="dropdownShow" class="input-dropdown">
<div class="dropdown-title">
{{ dropdownTitle }}
</div>
<div class="dropdown-content">
<ul class="content-list">
<li v-for="(item, index) in searchDataList" :key="index" @click="handleChoose(item.name)" class="list-item">
<span class="item-avatar">{{ item.avatar }}</span>
<span class="item-name">{{ item.name }}</span>
</li>
</ul>
<div v-show="dropdownMsgShow" class="content-msg">
{{ dropdownMsg }}
</div>
</div>
</div>
</div>
</template>
<script>
