一、組件使用場(chǎng)景及需求分析
表單多個(gè)固定單值的情況乌助,我們不用再去
input
框輸入值,直接在固定的值里面去選擇選擇以后父組件綁定的值對(duì)應(yīng)改變陌知,使得不需要發(fā)送表單前再進(jìn)行賦值
選擇前后他托,列表都是不可見(jiàn)的
二、開(kāi)始我們的codeing
首先我們需要的是一個(gè)有所有單值選項(xiàng)的list展示
然后是一個(gè)展示當(dāng)前選擇的文字框
像這樣的:
這一步我們只需要父組件傳遞單值代碼仆葡,然后當(dāng)前選中的一個(gè)值赏参,沒(méi)有的話就默認(rèn)為空。
vue:
<div class="fd-select-box">
<p v-text="scoped.selected&&scoped.selected.name?scoped.selected.name:'請(qǐng)選擇'"></p>
<span :class="fd-arrow icon iconfont"></span>
<ul class="fd-select-list">
<li v-for="(item,index) in list"
:key="index+'select'"
:class="{'active':scoped.selected&&item.code===scoped.selected.code}">
{{item.name}}</li>
</ul>
</div>
JS:
props: {
list: {
type: Array,
required: true,
},
selected: Object,
},
data() {
return {
scoped: {
// 當(dāng)前選中的
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;
}
}
}
}
這樣浙芙,我們已經(jīng)把外層框架搭建好了登刺。
接下來(lái),解決子組件改變嗡呼,父組件對(duì)應(yīng)的值發(fā)生改變纸俭。
一般情況我們會(huì)想到子組件把當(dāng)前選中的值通過(guò)this.$emit
傳給父組件,然后父組件再在對(duì)應(yīng)方法里面給對(duì)應(yīng)的值賦值南窗。今天我們用另外一種方法來(lái)解決這個(gè)問(wèn)題揍很,那就是v-model
,相信我,用了它你會(huì)愛(ài)上它万伤。
好了窒悔,我們看看官方文檔怎么說(shuō)的:
一個(gè)組件上的
v-model
默認(rèn)會(huì)利用名為value
的 prop 和名為input
的事件,但是像單選框敌买、復(fù)選框等類型的輸入控件可能會(huì)將value
attribute 用于不同的目的简珠。model
選項(xiàng)可以用來(lái)避免這樣的沖突:
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})
我的理解就是提供了v-model
;在自定義組件上model
里的prop
里的字段的值會(huì)直接賦給props
里面對(duì)應(yīng)字段虹钮,像之前我們給checked
傳值是在父組件上通過(guò):checked='false'
這樣一種形式×郑現(xiàn)在我們可以使用v-model='false'
。來(lái)看具體在select
框里面的表現(xiàn)吧芙粱。
export default {
name: 'fdSselect',
model: {
prop: 'selected',
event: 'changeValue',
},
props: {
list: {
type: Array,
required: true,
},
selected: Object,
},
data() {
return {
scoped: {
// 是否展示下面的列表
showFlag: false,
// 當(dāng)前選中的
selected: this.selected,
},
};
},
methods: {
// 值改變后傳給父組件祭玉,因?yàn)榻M件定義了model,所以父組件相當(dāng)于執(zhí)行了綁定的model值=emit出去的值
changeValue(item) {
this.scoped.selected = item;
this.scoped.showFlag = false;
this.$emit('changeValue', this.scoped.selected);
},
},
};
父組件調(diào)用:
<fd-select :list="selectList" v-model="selected"></fd-select>
上面的event
是我們要emit
出去的事件名春畔。這一步相當(dāng)于在父組件執(zhí)行了父組件的this.selected
等于子組件的this.scoped.selected
脱货;所以其實(shí)你用組件的時(shí)候v-model="value"
其實(shí)就是:value="value" @change="(val) => {value = val}"
;
現(xiàn)在看看我們實(shí)現(xiàn)的效果:
前兩個(gè)需求已經(jīng)實(shí)現(xiàn)了律姨,最后一個(gè)需求是在交互上的優(yōu)化振峻。
首先他要一開(kāi)始的時(shí)候不展示,我們給一個(gè)控制下拉框顯隱的變量择份。showFlag
默認(rèn)值為false
扣孟;點(diǎn)擊輸入框時(shí)展開(kāi)下拉列表。然后選中選項(xiàng)后隱藏下拉列表缓淹。
注意我們的頁(yè)面結(jié)構(gòu)哈打,下拉列表是輸入框的子元素塔逃,所以點(diǎn)擊下拉列表元素的時(shí)候會(huì)涉及到事件冒泡,這個(gè)時(shí)候我們使用.stop
修飾符來(lái)組織時(shí)間冒泡導(dǎo)致下拉列表一直不能隱藏料仗。
vue:
<div class="fd-select-box" @click="changeShow">
<p v-text="scoped.selected&&scoped.selected.name?scoped.selected.name:'請(qǐng)選擇'"></p>
<span :class="['fd-arrow icon iconfont',{'fd-down':scoped.showFlag}]"></span>
<ul class="fd-select-list" v-show="scoped.showFlag">
<li v-for="(item,index) in list"
:key="index+'select'"
@click.stop="changeValue(item)"
:class="{'active':scoped.selected&&item.code===scoped.selected.code}">
{{item.name}}</li>
</ul>
</div>
JS:
// 值改變后傳給父組件湾盗,因?yàn)榻M件定義了model,所以父組件相當(dāng)于執(zhí)行了綁定的model值=emit出去的值
changeValue(item) {
this.scoped.selected = item;
this.scoped.showFlag = false;
this.$emit('changeValue', this.scoped.selected);
},
// 改變下拉選項(xiàng)的顯隱
changeShow() {
this.scoped.showFlag = !this.scoped.showFlag;
},
繼續(xù)優(yōu)化立轧,我們現(xiàn)在實(shí)現(xiàn)了組件列表的顯隱格粪,但是只有操作當(dāng)前組件時(shí)可以控制。那么我們點(diǎn)擊其他地方的時(shí)候氛改,其實(shí)也是希望組件列表可以隱藏起來(lái)的帐萎。
實(shí)現(xiàn)這個(gè)的思路:綁定一個(gè)點(diǎn)擊事件在頁(yè)面上,只要點(diǎn)擊的元素不是當(dāng)前組件胜卤,那么我們就可以隱藏當(dāng)前組件的列表疆导。這里我用到了自定義指令,具體實(shí)現(xiàn)如下:
clickOutside: {
bind(el, binding) {
function clickHandler(e) {
// 這里判斷點(diǎn)擊的元素是否是本身葛躏,是本身澈段,則返回
if (el.contains(e.target)) {
return false;
}
// 判斷指令中是否綁定了函數(shù)
if (binding.expression) {
// 如果綁定了函數(shù) 則調(diào)用那個(gè)函數(shù),此處binding.value就是handleClose方法
binding.value(e);
}
return true;
}
// 給當(dāng)前元素綁定個(gè)私有變量舰攒,方便在unbind中可以解除事件監(jiān)聽(tīng)
el.vueClickOutside = clickHandler;
document.addEventListener('click', clickHandler);
},
unbind(el) {
// 解除事件監(jiān)聽(tīng)
document.removeEventListener('click', el.vueClickOutside);
delete el.vueClickOutside;
},
},
最后實(shí)現(xiàn)效果如圖:
后期待優(yōu)化:實(shí)現(xiàn)可搜索的下拉框-->實(shí)現(xiàn)可以遠(yuǎn)程搜索的下拉框
以上為個(gè)人編寫败富,希望能對(duì)大家的項(xiàng)目有所幫助,如有不當(dāng)以及有更好的方法歡迎交流摩窃。
項(xiàng)目地址: https://github.com/jasminezx/select.git