vue下拉列表的兩種實現(xiàn)方式比較
vue下拉列表的兩種實現(xiàn)
第一種采用v-for的方式
<el-select v-model="form.columeType" placeholder="字段類型"> <el-option v-for="(item,index) in columeTypeArr" :key="index" :label="item.label" :value="item.value"> </el-option> </el-select>
這種方式需要在data中定義columeTypeArr,如下
data(){ return { columeTypeArr:[{ value:'String', label:'字符串' },{ value:'Int', label:'整數', },{ value:'Decimal', label:'數值型' }], } }
第二種采用寫死的方式
直接在select下寫死
<el-select v-model="form.fileOrgType" placeholder="請選擇"> <el-option label="是" value="Y"> </el-option> <el-option label="否" value="N"></el-option> </el-select>
兩種方式的比較:
兩種方式都差不多,只是第一種方式需要在data中進行配置,對于需要數據從后臺回顯的情況,明顯是第一種方法好。
對于簡單的下拉列表參數很少的情況,第二種明顯更占優(yōu)。
vue下拉菜單組件的實現(xiàn)
我們一起來實現(xiàn)一個vue的下拉菜單組件。
像這種基本UI組件,網上已經有很多了,為什么要自己實現(xiàn)呢?其實并不是有意重復造輪子,而是想通過這個過程回顧一下vue組件開發(fā)的一些細節(jié)和注意事項。
為什么選擇下拉菜單組件?
因為:麻雀雖小五臟俱全,這個小小的組件涉及到了不少vue組件開發(fā)的知識點。
好了,那就開始吧!
首先創(chuàng)建一個vue-cli的項目,筆者用的是vue-cli3,創(chuàng)建過程略,然后創(chuàng)建一個vue組件:DropDownList.vue
在編寫模板之前,我們來分析一下這個組件的視圖結構和功能。
下拉菜單組件應該由兩部分組成:
選中項的文本
待選菜單(默認隱藏)
它的主要功能包括:
鼠標經過下拉菜單組件,顯示待選菜單
鼠標滑出下拉菜單組件,隱藏待選菜單
鼠標點擊待選菜單中的條目,選中項文本更新,組件派發(fā)change事件
我們編寫如下這樣的模板:
<template> <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)"> <span>選中項的文本<i></i></span> <ul> <li>北京</li> <li>上海</li> <li>廣州</li> </ul> </div> </template>
選中項文本右側的i標簽,用來實現(xiàn)下拉菜單的三角形圖標,在下文的css中我們用背景圖來實現(xiàn)。
我們給根元素div已經添加了鼠標經過和滑出的回調函數,具體實現(xiàn)見下文。
接下來我們?yōu)檫@個下拉菜單編寫樣式,在模板下方添加style標簽,為了防止和其他組件的樣式發(fā)生沖突,筆者建議大家在開發(fā)組件時,都給style加上scoped屬性。另外,筆者在這里用到了scss,具體代碼如下:
<style scoped lang="scss"> .zq-drop-list{ display: inline-block; min-width: 100px; position: relative; span{ display: block; height: 30px; line-height: 30px; background: #f1f1f1; font-size: 14px; text-align: center; color: #333333; border-radius: 4px; i{ background: url(https://www.easyicon.net/api/resizeApi.php?id=1189852&size=16) no-repeat center center; margin-left: 6px; display: inline-block; } } ul{ position: absolute; top: 30px; left: 0; width: 100%; margin: 0; padding: 0; border: solid 1px #f1f1f1; border-radius: 4px; overflow: hidden; li{ list-style: none; height: 30px; line-height: 30px; font-size: 14px; border-bottom: solid 1px #f1f1f1; background: #ffffff; } li:last-child{ border-bottom: none; } li:hover{ background: #f6f6f6; } } } </style>
關于樣式,這里就不詳細展開了,只說其中幾個需要注意的點:
那個i元素的樣式,我用到了一個網絡圖片,大家可以自行更換
待選菜單ul在css里并沒有讓它隱藏,因為我們要通過js來控制,具體原因見下文
待選菜單ul使用了絕對定位,因為當它展開的時候,不應該影響頁面上其他元素的布局
現(xiàn)在這個組件大概長這個樣子:
我們繼續(xù)為這個組件定義屬性,很顯然,待選菜單應該作為屬性傳進來,一定不能是內部寫死的,屬性定義如下:
<script> export default { name: "DropDownList", props:{ dataList:{ type:Array, default(){ return [ {name: "選項一"}, {name: "選項二"} ] } }, labelProperty:{ type:String, default(){ return "name" } } }, data(){ return { activeIndex:0 } }, }
其中dataList就是待選菜單的數據源屬性,這里我們給這個屬性定義了默認值,這也是筆者建議大家養(yǎng)成的一個習慣,作為一個組件,最好有默認值,因為當別人使用你的組件時,可以先不設置相關屬性,就能看到一個成品的效果,也能快速查看你這個組件所需屬性的數據細節(jié)。
另外一個屬性是labelProperty,這個屬性的作用是什么?我們實際項目中的數據源,并不一定都含有name這個字段,因此就可能導致下拉菜單無法渲染數據的文本,于是我們定義了這個屬性用來指定實際數據源渲染文本的字段,這個字段必須是字符串。這個屬性的默認值是name,因為它需要和默認數據源保持一致。相信你還看到了一個組件內部數據,activeIndex,這個是用來表示當前選中項的索引的,我們后面會用到。
現(xiàn)在我們就可以在其他地方引入并使用這個組件了,雖然它還沒有完成,但我們不妨先讓它顯示在界面上吧:
<template> <div class="home"> <DropList :dataList="dplist" labelProperty="city" @change="onDpChange($event)"></DropList> <p>其他文本內容</p> </div> </template> <script> import DropList from '@/components/DropDownList.vue' //其他代碼略 </script>
這個頁面引入并使用了我們的DropDownList組件,:dataList="dplist" 綁定了當前頁面的dplist數組到組件的dataList屬性上,這個數組中的對象有一個city字段,我們希望此字段顯示在下拉菜單上,因此我們設置組件的labelProperty為city,我們還給這個組件注冊了change事件,這個組件內部需要派發(fā)這個事件,見下文。
現(xiàn)在我們回到組件的模板部分,發(fā)現(xiàn)它都還是靜態(tài)內容,我們把這些靜態(tài)內容修改為通過屬性渲染。
<template> <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)"> <span>{{dplLable}}<i></i></span> <ul> <li v-for="(item, index) in dataList" :key="index" @click="onLiClick(index, $event)">{{item[labelProperty]}}</li> </ul> </div> </template>
其中待選菜單li的文本是 item[labelProperty] 這樣就能正確的顯示開發(fā)者指定的字段了。
我們看看選中項的文本表達式:dplLabel,我們并沒有定義這個屬性,也沒有定義這個內部數據,它是哪兒來的?選中項的文本應該是 dataList[activeIndex][labelProperty] (這個很好理解吧,有問題請留言),但這個表達式太長了,寫在模板里不利于維護,我們就把它寫到計算屬性里吧。
computed:{ dplLable(){ return this.dataList[this.activeIndex][this.labelProperty] } }
于是才有了上面的dplLabel,計算屬性真的很好用呢。
現(xiàn)在下拉菜單的視圖和數據關聯(lián)部分我們已經寫完了,接下來我們要實現(xiàn)它的功能。
第一步是先讓待選菜單默認隱藏起來,這里我們?yōu)槭裁床恢苯佑胏ss的display:none呢,然后鼠標經過的時候display:block不就可以了嗎?因為這樣的話,我們無法實現(xiàn)點擊待選菜單條目的時候讓它隱藏,體驗不好。我們用js來控制,但vue對直接訪問dom元素支持的并不好,我們要想在組件初始化的時候訪問dom元素,有一個最方便的做法,那就是:自定義指令。
我們?yōu)橄吕藛谓M件添加局部自定義指令,代碼如下:
directives:{ dpl:{ bind(el){ el.style.display = "none"; } } },
這個dpl就是自定義指令啦,請忽略我笨拙的命名哈!然后我們在自定義指令的鉤子函數bind方法中,訪問el元素,控制它的style屬性display:none; 最后,把這個自定義指令加到模板里面的ul標簽上。別忘了要加v-,現(xiàn)在看看效果,待選菜單已經隱藏了。
<ul v-dpl>
我們利用自定義指令鉤子函數訪問dom元素,實現(xiàn)了對dom的控制,這一點非常實用!
讓我們繼續(xù)實現(xiàn)最開始為下拉菜單定義的鼠標經過和鼠標滑出的監(jiān)聽,實現(xiàn)待選菜單的顯示與隱藏。
onDplOver(event){ let ul = event.currentTarget.childNodes[1]; ul.style.display = "block"; }, onDplOut(event){ let ul = event.currentTarget.childNodes[1]; ul.style.display = "none"; },
我們在鼠標事件中,訪問event的currentTarget對象,為什么不是target?因為下拉菜單的子元素也會觸發(fā)這個事件,如果訪問target,可能不會是我們預期的頂層元素。
最后一步,我們實現(xiàn)待選菜單條目的點擊事件,點擊后,待選菜單隱藏,修改內部狀態(tài),派發(fā)change事件。
onLiClick(index){ let path = event.path || (event.composedPath && event.composedPath()) //兼容火狐和safari path[1].style.display = "none"; this.activeIndex = index; this.$emit("change", { index:index, value:this.dataList[index] }) }
這里有一個細節(jié)需要注意,我們要通過li元素找到外層ul元素,但path不支持火狐和safari,好在這兩個瀏覽器支持composedPath,因此才有了第一行代碼的兼容寫法。然后通過修改內部數據activeIndex實現(xiàn)選中項文本的更新,最后調用emit方法向父元素派發(fā)change事件,別忘了把事件對象封裝好傳出去。
完整的代碼如下:
<template> <div class="zq-drop-list" @mouseover="onDplOver($event)" @mouseout="onDplOut($event)"> <span>{{dplLable}}<i></i></span> <ul v-dpl> <li v-for="(item, index) in dataList" :key="index" @click="onLiClick(index, $event)">{{item[labelProperty]}}</li> </ul> </div> </template>
<script> export default { name: "DropDownList", data(){ return { activeIndex:0 } }, props:{ dataList:{ type:Array, default(){ return [ {name: "選項一"}, {name: "選項二"} ] } }, labelProperty:{ type:String, default(){ return "name" } } }, directives:{ dpl:{ bind(el){ el.style.display = "none"; } } }, methods:{ onDplOver(event){ let ul = event.currentTarget.childNodes[1]; ul.style.display = "block"; }, onDplOut(event){ let ul = event.currentTarget.childNodes[1]; ul.style.display = "none"; }, onLiClick(index){ let path = event.path || (event.composedPath && event.composedPath()) //兼容火狐和safari path[1].style.display = "none"; this.activeIndex = index; this.$emit("change", { index:index, value:this.dataList[index] }) } }, computed:{ dplLable(){ return this.dataList[this.activeIndex][this.labelProperty] } } } </script>
<style scoped lang="scss"> .zq-drop-list{ display: inline-block; min-width: 100px; position: relative; span{ display: block; height: 30px; line-height: 30px; background: #f1f1f1; font-size: 14px; text-align: center; color: #333333; border-radius: 4px; i{ background: url(https://www.easyicon.net/api/resizeApi.php?id=1189852&size=16) no-repeat center center; margin-left: 6px; display: inline-block; } } ul{ position: absolute; top: 30px; left: 0; width: 100%; margin: 0; padding: 0; border: solid 1px #f1f1f1; border-radius: 4px; overflow: hidden; li{ list-style: none; height: 30px; line-height: 30px; font-size: 14px; border-bottom: solid 1px #f1f1f1; background: #ffffff; } li:last-child{ border-bottom: none; } li:hover{ background: #f6f6f6; } } } </style>
以上為大家展示了vue如何實現(xiàn)一個下拉菜單組件,雖然比較簡單,但也基本涉及到了組件開發(fā)常用的一些特性。希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
vue 在methods中調用mounted的實現(xiàn)操作
這篇文章主要介紹了vue 在methods中調用mounted的實現(xiàn)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Node.js使用orm2進行update操作時關聯(lián)字段無法修改的解決方法
這篇文章主要給大家介紹了Node.js使用orm2進行update操作時關聯(lián)字段無法修改的解決方法,文中給出了詳細的示例代碼供大家參考學習,對大家具有一定的參考學習價值,需要的朋友們下面來一起看看吧。2017-06-06