Vue自定義Form組件實(shí)現(xiàn)方法介紹
1. 需求分析
我們要把我們的表單組件分成兩個(gè)部分,一個(gè)是item部分,一個(gè)是整體的 form 部分,form部分由item和button提交按鈕共同組成。
在我們單擊每個(gè)輸入框時(shí)會(huì)觸發(fā)每一個(gè)item的驗(yàn)證規(guī)則,然后點(diǎn)擊登錄按鈕會(huì)驗(yàn)證整個(gè) form 。
2. 表單功能的簡(jiǎn)單實(shí)現(xiàn)
我們先去 bootstrap 文檔里找到 form 表單然后把它的模板代碼 copy 過(guò)來(lái),當(dāng)然前提是我們首先要在項(xiàng)目中安裝 BootStrap。
現(xiàn)在運(yùn)行我們的項(xiàng)目,就能看到 form 表單的樣式了:
首先我們通過(guò) reactive 來(lái)綁定每個(gè)輸入框需要綁定的數(shù)據(jù):
const emailRef = reactive({ val: '', error: false, message: '' })
然后通過(guò) v-model 和我們剛剛定義的數(shù)據(jù)進(jìn)行雙向綁定:
我們又定義了一個(gè) validateEmail 函數(shù)當(dāng)鼠標(biāo)失去焦點(diǎn)時(shí)觸發(fā),我們?cè)谶@個(gè)方法中定義輸入框的驗(yàn)證標(biāo)準(zhǔn):
const emailReg = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/ const validateEmail = () => { if (emailRef.val.trim() === '') { emailRef.error = true emailRef.message = '輸入內(nèi)容不能為空' } else if (!emailReg.test(emailRef.val)) { emailRef.error = true emailRef.message = '輸入郵箱格式不正確' } else { emailRef.error = false } }
現(xiàn)在當(dāng)我們什么也不輸入時(shí),輸入框效果:
輸入郵箱格式不正確時(shí),輸入框效果:
3. 抽象驗(yàn)證規(guī)則
剛剛我們完成了郵箱的驗(yàn)證邏輯,我們還得做密碼框的驗(yàn)證邏輯,如果我們的表單還有很多功能不一樣的輸入框,那我們得挨個(gè)給他們添加驗(yàn)證功能,這樣就有大量重復(fù)代碼,要寫非常多冗余的變量和函數(shù),我們作為開(kāi)發(fā)者最忌諱的就是復(fù)制粘貼做搬運(yùn)工,所以我們就想把這部分的邏輯抽離出去作為一個(gè)可復(fù)用的組件
我們輸入框的組件就是圖片中的 validate-input,如果我們只需要在父組件中輸入要驗(yàn)證的規(guī)則和驗(yàn)證失敗的信息,把邏輯交給 validate-input 來(lái)判斷,那整體代碼就非常清晰了。
我們通過(guò) rules 屬性來(lái)傳給組件指定驗(yàn)證類型。message字段是出現(xiàn)問(wèn)題時(shí)提示的內(nèi)容,因?yàn)槲覀兊妮斎肟蚪M件可以使用不止一種規(guī)則,所以 RulesProp 應(yīng)該是 RuleProp 的數(shù)組。如果以后要添加其他的規(guī)則,就可以直接在下面的 type 中添加,這樣可擴(kuò)展性非常高。
interface RuleProp { type: 'required' | 'email'; message: string; } export type RulesProp = RuleProp[]
我們?cè)谧咏M件中定義規(guī)則的接口,然后定義都是這種類型的數(shù)組結(jié)構(gòu)并把它導(dǎo)出出去方便父組件使用。如果不熟習(xí) typescript 的朋友,就可以把它當(dāng)作定義一個(gè)RuleProp對(duì)象,里面有兩個(gè)屬性,一個(gè)是規(guī)則類型,一個(gè)是出現(xiàn)問(wèn)題時(shí)提示的內(nèi)容。然后再定義一個(gè)對(duì)象數(shù)組,把它導(dǎo)出出去這樣父組件向子組件傳遞的都是這種規(guī)定的對(duì)象數(shù)組。
子組件接受的 props把它斷言成 RulesProp 類型的數(shù)組:
我們?cè)倏匆幌?validate-input 的邏輯部分:
setup (props) { const inputRef = reactive({ val: '', error: false, message: '' }) const validateInput = () => { if (props.rules) { const allPassed = props.rules.every(rule => { let passed = true inputRef.message = rule.message switch (rule.type) { case 'required': passed = (inputRef.val.trim() !== '') break case 'email': passed = emailReg.test(inputRef.val) break default: break } return passed }) inputRef.error = !allPassed } } return { inputRef, validateInput } }
我們先定義一個(gè) inputRef 對(duì)象來(lái)綁定輸入信息和狀態(tài)。validateInput 當(dāng)輸入框失去焦點(diǎn)的時(shí)候觸發(fā)這個(gè)驗(yàn)證函數(shù)。下面我們來(lái)看一下這個(gè)函數(shù)的實(shí)現(xiàn)邏輯:
首先通過(guò)一個(gè) if 實(shí)現(xiàn)當(dāng)有 props 的時(shí)候才做驗(yàn)證。然后通過(guò)數(shù)組的 every 方法來(lái)給每一個(gè)數(shù)組中的每一個(gè)驗(yàn)證對(duì)象做判定,every 方法如果全部為真時(shí)才為真,有一個(gè)為假就是假。他很符合表單驗(yàn)證的邏輯,所以最后 every 方法一定會(huì)返回 true 或者 false,我們讓一個(gè)變量接受它,如果這個(gè)變量是真就代表輸入框全部驗(yàn)證規(guī)則都通過(guò),那么 inputRef 的 error 屬性就是 false,這樣就不會(huì)如下的錯(cuò)誤提示:
<span v-if="inputRef.error" class="invalid-feedback">{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->{inputRef.message}}</span>
在 every 方法中,我們一項(xiàng)一項(xiàng)判斷,先設(shè)置當(dāng)前項(xiàng)返回 true ,然后把當(dāng)前項(xiàng)的 message ,也就是錯(cuò)誤提示內(nèi)容賦值給 inputRef 的 message,然后通過(guò) switch 來(lái)判定當(dāng)前項(xiàng)的狀態(tài),這樣當(dāng)當(dāng)前項(xiàng)不滿足規(guī)則時(shí),返回的 message 就是當(dāng)前項(xiàng)的 message
如果最后 inputRef.error 是 false,那么就添加 bootstrap 中表單錯(cuò)誤類,來(lái)實(shí)現(xiàn)動(dòng)態(tài)綁定:
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" v-model="inputRef.val" :class="{'is-invalid':inputRef.error}" @blur = "validateInput" >
到這里我們就實(shí)現(xiàn)了子組件的處理,下面只需要在父組件中把參數(shù)傳過(guò)去就行了:
const emailRules: RulesProp = [ { type: 'required', message: '電子郵箱地址不能為空' }, { type: 'email', message: '請(qǐng)輸入正確的電子郵箱格式' } ] const passwordRules: RulesProp = [ { type: 'required', message: '輸入密碼不能為空' } ]
我們定義兩個(gè) RulesProp 類型的數(shù)組作為參數(shù),傳遞給子組件:
現(xiàn)在啟動(dòng)項(xiàng)目,看一下效果:
這樣我們這一節(jié)抽離驗(yàn)證規(guī)則的目的就達(dá)到了。
4. 支持 v-model 雙向綁定
現(xiàn)在我們已經(jīng)把驗(yàn)證規(guī)則抽離出來(lái),實(shí)現(xiàn)了表單的基本驗(yàn)證,但是有一個(gè)痛點(diǎn)需要我們解決,我們?cè)诟附M件中現(xiàn)在拿不到用戶在輸入框中輸入的值,這樣就實(shí)現(xiàn)不了下一步的其他需求,在 input 中我們通過(guò) v-model 指令來(lái)進(jìn)行雙向綁定可以很輕松地獲得用戶輸入的值,那么在 validate-input 組件中我們?nèi)绾蝸?lái)實(shí)現(xiàn) v-model 呢?
我們先看一下 vue3 中 v-model 的實(shí)現(xiàn)原理:
vue3 中摒棄了 vue2 里通過(guò)動(dòng)態(tài)綁定 input 的 value 屬性和 input 事件實(shí)現(xiàn)的雙向綁定,通過(guò) modelValue 這么個(gè)屬性和 onUpdate:modelValue 這個(gè)事件來(lái)實(shí)現(xiàn)雙向綁定。所以我們要實(shí)現(xiàn) v-model 就要有上面的屬性和更新輸入框的時(shí)候出發(fā)的事件。
首先我們來(lái)寫 validate-input 子組件中的內(nèi)容,在 props 參數(shù)中加入 modelValue:
然后用 :value 和 input 事件把原來(lái) input 中的 v-model 替換一下:
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" :value="inputRef.val" :class="{'is-invalid':inputRef.error}" @blur = "validateInput" @input="updateValue" >
定義 input 事件,注意提交的事件名為 update:modelValue:
const updateValue = (e: KeyboardEvent) => { const targetValue = (e.target as HTMLInputElement).value inputRef.val = targetValue context.emit('update:modelValue', targetValue) }
我們?cè)诟附M件中使用一下 v-model ,看看效果:
可以看到我們成功實(shí)現(xiàn)了 v-model 的雙向綁定:
5. 使用 $attrs 支持默認(rèn)屬性
在原生 input 中有很多屬性,比如 placeholder ,如果我們?cè)谖覀兊妮斎肟蚪M件中添加這個(gè)屬性,會(huì)正常顯示在頁(yè)面上嗎?我們?cè)囈幌拢?/p>
啟動(dòng)項(xiàng)目,查看效果:
placeholder沒(méi)有正常顯示出來(lái),我們查看控制臺(tái),看看哪里有問(wèn)題:
可以看到我們的placeholder被直接添加到 input 的父級(jí)上了,那如何把屬性正確添加在 input 上呢?
1. 首先我們只需要在組件的選項(xiàng)中設(shè)置 inheriAttrs: false:
2. 通過(guò) $attrs 把屬性添加到元素上:
我們先輸出一下 $attrs 看看里面有什么:
這是一個(gè)響應(yīng)式對(duì)象,里面包括了我們傳遞給子組件的屬性
下面我們先通過(guò) v-bind 綁定 $attrs :
給我們的 input 組件添加屬性:
啟動(dòng)項(xiàng)目,查看輸出:
現(xiàn)在成功給組件添加了 placeholder 屬性,也成功添加了 type ,密碼框也變成了小圓點(diǎn)。
6. 父組件調(diào)用子組件中的方法
現(xiàn)在我們要實(shí)現(xiàn)的就是點(diǎn)擊提交按鈕,然后分別進(jìn)行兩個(gè)輸入框的驗(yàn)證??墒俏覀兊尿?yàn)證方法在 validate-input 這個(gè)子組件中,所以我們就得在父組件中調(diào)用子組件里的方法來(lái)實(shí)現(xiàn)表單驗(yàn)證。
1. 給子組件添加 ref 屬性:
2. 給提交按鈕添加點(diǎn)擊事件:
3. 在 setup 中定義響應(yīng)式對(duì)象及點(diǎn)擊事件:
const emailChild = ref<InstanceType<typeof ValidateInput>>() const passwordChild = ref<InstanceType<typeof ValidateInput>>() const ensureForm = () => { emailChild.value?.validateInput() passwordChild.value?.validateInput() }
這樣我們就成功調(diào)用了子組件中的 validateInput 方法,實(shí)現(xiàn)了我們想要的效果。
4. 啟動(dòng)項(xiàng)目,查看效果:
直接點(diǎn)擊提交按鈕:
輸入錯(cuò)誤郵箱格式,點(diǎn)擊提交:
到這里我們的表單組件就開(kāi)發(fā)完成了。
到此這篇關(guān)于Vue自定義Form組件實(shí)現(xiàn)方法介紹的文章就介紹到這了,更多相關(guān)Vue自定義Form組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue中JS動(dòng)畫與Velocity.js的結(jié)合使用
這篇文章主要介紹了Vue中JS動(dòng)畫與Velocity.js的結(jié)合使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02解決vue?vite啟動(dòng)項(xiàng)目報(bào)錯(cuò)ERROR:?Unexpected?“\x88“?in?JSON?的問(wèn)題
這篇文章主要介紹了vue?vite啟動(dòng)項(xiàng)目報(bào)錯(cuò)ERROR:?Unexpected?“\x88“?in?JSON?原因,本文給出出現(xiàn)此類問(wèn)題的原因所在并給出解決方法,需要的朋友可以參考下2022-09-09有關(guān)vue 組件切換,動(dòng)態(tài)組件,組件緩存
這篇文章主要介紹了有關(guān)vue 組件切換,動(dòng)態(tài)組件,組件緩存,在組件化開(kāi)發(fā)模式下,我們會(huì)把整個(gè)項(xiàng)目拆分成很多組件,然后按照合理的方式組織起來(lái),達(dá)到預(yù)期效果,下面來(lái)看看文章的詳細(xì)內(nèi)容2021-11-11Vuex(多組件數(shù)據(jù)共享的Vue插件)搭建與使用
Vuex是實(shí)現(xiàn)組件全局狀態(tài)(數(shù)據(jù))管理的一種機(jī)制,可以方便的實(shí)現(xiàn)組件之間數(shù)據(jù)的共享,數(shù)據(jù)緩存等等,下面這篇文章主要給大家介紹了關(guān)于Vuex(多組件數(shù)據(jù)共享的Vue插件)搭建與使用的相關(guān)資料,需要的朋友可以參考下2022-10-10vue中filters 傳入兩個(gè)參數(shù) / 使用兩個(gè)filters的實(shí)現(xiàn)方法
這篇文章主要介紹了vue中filters 傳入兩個(gè)參數(shù) / 使用兩個(gè)filters的實(shí)現(xiàn)方法,文中給大家提到了Vue 中的 filter 帶多參的使用方法,需要的朋友可以參考下2019-07-07vue watch普通監(jiān)聽(tīng)和深度監(jiān)聽(tīng)實(shí)例詳解(數(shù)組和對(duì)象)
這篇文章主要介紹了vue watch普通監(jiān)聽(tīng)和深度監(jiān)聽(tīng)(數(shù)組和對(duì)象),文中單獨(dú)通過(guò)代碼給大家介紹了vue watch 深度監(jiān)聽(tīng)的方法,感興趣的朋友一起看看吧2018-08-08