Vue?Validate表單組件的封裝詳解
之前一直是直接去使用別人現(xiàn)成的組件庫,也沒有具體去了解人家的組件是怎么封裝的,造輪子才會(huì)更好地提高自己,所以嘗試開始從封裝Form表單組件開始
一:組件需求分析
本次封裝組件,主要是摸索封裝組件的流程,對(duì)于具體需要的方法和屬性,只會(huì)實(shí)現(xiàn)其中部分方法和屬性,之后一點(diǎn)一點(diǎn)才進(jìn)行添加
表單項(xiàng)組件,ValidateInput組件的封裝
- 根據(jù)傳遞不同的type類型有著不同的校驗(yàn)規(guī)則
- 支持V-model,對(duì)于封裝的自定義組件支持v-model雙向綁定也是一個(gè)很關(guān)鍵的屬性
- tag根節(jié)點(diǎn)的類型
form組件,提交事件
- 驗(yàn)證整體表單項(xiàng),是否通過
- 清空表單項(xiàng)的內(nèi)容
二:技術(shù)棧
Vue3
TS
Bootstrap樣式庫
三:封裝Validate-input驗(yàn)證表單項(xiàng)組件
思路
1. 明確屬性和事件
- v-model屬性
- rules屬性:根據(jù)不同type不同校驗(yàn)規(guī)則
- tag根節(jié)點(diǎn)類型
ValidateInput組件
<template> <div class="validate-input-container pb-3"> <input class="form-control" :class="{ 'is-invalid': inputRef.error }" :value="modelValue" @blur="validateInput" @input="updateValue" v-bind="$attrs" /> <span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span> </div> </template>
<script setup lang="ts"> //禁用 Attributes 繼承 defineOptions({ inheritAttrs: false }) //定義傳來的一個(gè)參數(shù)類型 interface RangeProp { message: string length: number } interface RuleProp { type: 'required' | 'email' | 'range' message?: string //至少位數(shù) min?: RangeProp max?: RangeProp } //數(shù)組類型 export type RulesProp = RuleProp[] //接收參數(shù) const props = defineProps<{ rules?: RulesProp //自定義組件使用v-model需要用modelValue來接收參數(shù) modelValue: string }>() //定義表單的數(shù)據(jù) const inputRef = reactive({ //如果為空 val: props.modelValue || '', error: false, //表單驗(yàn)證是否通過 message: '' //錯(cuò)誤信息 }) </script>
禁用Attributes繼承:透傳 attribute”指的是傳遞給一個(gè)組件,卻沒有被該組件聲明為 props 或 emits 的 attribute 或者 v-on
事件監(jiān)聽器。最常見的例子就是 class
、 style
和 id
。
<!-- <MyButton> 的模板 --> <button>click me</button>
當(dāng)父組件使用該組件,并且傳入class:
<MyButton class="large" />
最終會(huì)在根元素出現(xiàn)class=‘large“, <MyButton>
并沒有將 class
聲明為一個(gè)它所接受的 prop,所以 class
被視作透傳 attribute,自動(dòng)透傳到了 <MyButton>
的根元素上。
因此需要禁用Attibutes繼承
定義接收參數(shù)類型
- rules可選參數(shù),接收數(shù)組
- modelValue:接收字符串即輸入的值
2. v-model屬性
在使用一個(gè)自定義組件給其添加v-model屬性,其自定義組件內(nèi)部做了兩件事
將內(nèi)部原生的input元素的值綁定到組件內(nèi)部Prop屬性modelValue
當(dāng)原生input元素觸發(fā)時(shí)候,觸發(fā)一個(gè)攜帶了新值的 update:modelValue
自定義事件
<!-- 自定義組件Validate-input --> <Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="請(qǐng)輸入郵箱地址"></Validate-input>
Validate-input組件
//接收參數(shù) const props = defineProps<{ rules?: RulesProp //自定義組件使用v-model需要用modelValue來接收參數(shù) modelValue: string }>()
props接收了,modelValue屬性,類型string
<input class="form-control" :class="{ 'is-invalid': inputRef.error }" :value="modelValue" @blur="validateInput" @input="updateValue" v-bind="$attrs" />
設(shè)置了自定義事件用于更新value
//手動(dòng)更新value const updateValue = (e: Event) => { //HTMLInputElement輸入元素類型 let targetValue = (e.target as HTMLInputElement).value //更新文本框的值 inputRef.val = targetValue emit('update:modelValue', targetValue) }
因此實(shí)現(xiàn)v-model屬性
3. rules屬性
接收參數(shù)
- type:校驗(yàn)類型,requied | email | range
- message
- min:至少幾位
- max:至多幾位
抽象驗(yàn)證邏輯
validate-input組件
//數(shù)組類型 export type RulesProp = RuleProp[] //接收參數(shù) const props = defineProps<{ rules?: RulesProp //自定義組件使用v-model需要用modelValue來接收參數(shù) modelValue: string }>()
參數(shù)接收rules是個(gè)數(shù)組
//定義表單的數(shù)據(jù) const inputRef = reactive({ //如果為空 val: props.modelValue || '', error: false, //表單驗(yàn)證是否通過 message: '' //錯(cuò)誤信息 })
定義表單的數(shù)據(jù)
//定義事件 const validateInput = () => { //定義郵箱的正則 let emailReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/ //如果傳過來的有驗(yàn)證的話 if (props.rules) { //every方法 遍歷全部數(shù)組只有全部滿足條件才會(huì)返回true, 只要有一個(gè)false停止遍歷 let allRulePassed = props.rules.every(rule => { let passed = true //消息賦值 類型斷言 inputRef.message = rule.message as string //switch選擇對(duì)應(yīng)type類型錯(cuò)誤進(jìn)行校驗(yàn) switch (rule.type) { case 'required': if (inputRef.val.trim() === '') { passed = false } break case 'email': if (!emailReg.test(inputRef.val)) { passed = false } break case 'range': passed = validateRange(rule.min, rule.max) break } return passed }) //allRulePassed為false表示通過 //所以Input.error表示錯(cuò)誤應(yīng)該為true inputRef.error = !allRulePassed return allRulePassed } return true }
定義校驗(yàn)事件
滿足全部rules校驗(yàn)才可以通過,因此用到es6數(shù)組方法,every只有全部項(xiàng)都為true才會(huì)遍歷全部返回true,只要有一個(gè)項(xiàng)結(jié)果為false,就會(huì)停止遍歷
message校驗(yàn)信息賦值給inputRef.message
利用swicth case語句,選擇對(duì)應(yīng)的type類型進(jìn)行校驗(yàn)
需要充分考慮到所有情況,通過passed為true,未通過為fasle
range長度校驗(yàn),由于情況較多,單獨(dú)封裝一個(gè)函數(shù)去校驗(yàn)
//校驗(yàn)長度 const validateRange = (min: RangeProp | undefined, max: RangeProp | undefined) => { let passed = true //1. 如果min 存在 ,max不存在 if (min && !max) { inputRef.message = min.message passed = !(inputRef.val.length < min.length) } //2. min不在, max在 if (!min && max) { inputRef.message = max.message passed = !(inputRef.val.length > max.length) } //3. min在 max在 if (min && max) { //若小于 if (inputRef.val.length < min.length) { passed = false inputRef.message = min.message } //若大于 if (inputRef.val.length > max.length) { passed = false inputRef.message = max.message } } return passed }
模板中,根據(jù)passed的值,動(dòng)態(tài)綁定未通過的樣式
4. 默認(rèn)屬性
validate-input組件
<div class="validate-input-container pb-3"> <input class="form-control" :class="{ 'is-invalid': inputRef.error }" :value="modelValue" @blur="validateInput" @input="updateValue" v-bind="$attrs" /> <span v-if="inputRef.error" class="invalid-feedback">{{ inputRef.message }}</span> </div>
父組件
<Validate-input v-model="loginParams.password" :rules="passwordRules" type="password" placeholder="請(qǐng)輸入密碼" ></Validate-input>
通過顯示綁定attrs,使得自定義組件可以使用默認(rèn)屬性
在 ValidateInput
組件中,可以通過 $attrs
屬性將 type
屬性將被傳遞到組件的根元素上
可以在父組件中靈活地傳遞任何HTML屬性給 ValidateInput
組件,使它更加通用和可配置
四:封裝Validate-Form組件
事件
form-submit:點(diǎn)擊提交觸發(fā)的事件,回調(diào)參數(shù)result是布爾值,表示該表單是否通過了校驗(yàn)
1. form-submit事件
當(dāng)點(diǎn)擊提交時(shí),要去驗(yàn)證該表單是否通過了校驗(yàn),就需要一個(gè)個(gè)將表單項(xiàng)進(jìn)行校驗(yàn),只要一個(gè)沒通過就返回false,只有全部通過返回true
收集全部表單項(xiàng)校驗(yàn)函數(shù)
可以通過將所有表單項(xiàng)的校驗(yàn)函數(shù)都添加到一個(gè)數(shù)組中,然后最終通過every方法遍歷是否全部通過
收集
安裝mitt并使用,監(jiān)聽事件
npm install mitt --save
全局掛載
main.ts
import mitt from 'mitt' //對(duì)外暴露全局事件總線實(shí)例 export const bus = mitt()
ValidateForm組件中使用
<script setup lang="ts"> import { onUnmounted, reactive } from 'vue' import { bus } from '@/main' const emits = defineEmits(['form-submit']) //點(diǎn)擊事件 //定義函數(shù)類型 type ValidateFunc = () => boolean //定義類型 //定義接收的函數(shù)數(shù)組 const funcArr = reactive<ValidateFunc[]>([]) //測(cè)試回調(diào) const callback = (func: ValidateFunc) => { //將每個(gè)校驗(yàn)函數(shù)加入數(shù)組 funcArr.push(func) } //訂閱事件 bus.on('form-item-created', callback as ValidateFunc) //提交按鈕觸發(fā)事件 const submitForm = () => { const result = funcArr.map(func => func()).every(result => result) // 觸發(fā)提交事件 //遍歷funcArr中的每個(gè)校驗(yàn)函數(shù) emits('form-submit', result) } onUnmounted(() => { // 移除訂閱 bus.off('form-item-created', callback as ValidateFunc) }) </script>
- 定義函數(shù)類型,返回值為空
- 利用泛型定義存放每個(gè)表單項(xiàng)校驗(yàn)函數(shù)存放的數(shù)組
- 訂閱事件,在組件卸載的時(shí)候再取消訂閱
ValidateInput 組件中
組件掛載的時(shí)候,就直接觸發(fā)自定義事件,然后將每一項(xiàng)的校驗(yàn)函數(shù)傳遞給 ValidateForm組件
中
Validate-Form組件
然后接受ValidateInput組件傳遞過來的校驗(yàn)函數(shù),一個(gè)個(gè)加入到數(shù)組中
在submitForm事件中,遍歷數(shù)組,不能直接使用every方法,因?yàn)橹灰粋€(gè)不通過就不進(jìn)行后面的校驗(yàn),這是不滿足我們的需求的
可以看到下面的錯(cuò)誤實(shí)例,密碼原本也是不通過校驗(yàn)的,但是every方法直接在第一個(gè)校驗(yàn)失敗結(jié)束遍歷了
因此先利用map函數(shù),先使得每個(gè)校驗(yàn)函數(shù)都執(zhí)行,結(jié)束后返回一個(gè)新的數(shù)組存放校驗(yàn)函數(shù)的返回值,再通過every遍歷數(shù)組
最后觸發(fā)自定義事件,將結(jié)果傳遞給父組件中
父組件
<Validate-Form @form-submit="submitForm" ref="validateFormRef">
const submitForm = (result: boolean) => { console.log(result) }
提交表單元素后清空表單值
具體邏輯跟校驗(yàn)差不多
Validate-Form組件
<script setup lang="ts"> import { onUnmounted, reactive } from 'vue' import { bus } from '@/main' const emits = defineEmits(['form-submit']) //點(diǎn)擊事件 //定義函數(shù)類型 type ValidateFunc = () => boolean //定義清空Input函數(shù)類型 type clearInputsFunc = () => void //定義類型 //定義接收的函數(shù)數(shù)組 const funcArr = reactive<ValidateFunc[]>([]) //定義接收用于清空的函數(shù)數(shù)組 const clearFuncArr = reactive<clearInputsFunc[]>([]) //測(cè)試回調(diào) const callback = (func: ValidateFunc) => { //將每個(gè)校驗(yàn)函數(shù)加入數(shù)組 funcArr.push(func) } //事件回調(diào) const clearInputFunc = (func: clearInputsFunc) => { clearFuncArr.push(func) } //綁定監(jiān)聽事件 bus.on('form-item-created', callback as ValidateFunc) bus.on('form-item-clear', clearInputFunc as clearInputsFunc) //提交按鈕觸發(fā)事件 const submitForm = () => { const result = funcArr.map(func => func()).every(result => result) // 觸發(fā)提交事件 //遍歷funcArr中的每個(gè)校驗(yàn)函數(shù) emits('form-submit', result) //遍歷清空函數(shù)數(shù)組并依次并執(zhí)行 //當(dāng)校驗(yàn)通過時(shí)候才會(huì)清空input if (result) { clearFuncArr.map(func => func()) } } onUnmounted(() => { // 移除事件監(jiān)聽器 bus.off('form-item-created', callback as ValidateFunc) bus.off('form-item-clear', clearInputFunc as clearInputsFunc) }) </script>
Validate-input組件
onMounted(() => { //直接把validateInput校驗(yàn)事件傳遞過去 bus.emit('form-item-created', validateInput) //直接觸發(fā)事件先傳入每個(gè)input的值 bus.emit('form-item-clear', clearInput) }) //定義表單的數(shù)據(jù) const inputRef = reactive({ //如果為空 val: props.modelValue || '', error: false, //表單驗(yàn)證是否通過 message: '' //錯(cuò)誤信息 })
把清空表單數(shù)據(jù)的處理函數(shù)收集起來
最后利用map方法依次執(zhí)行清除即可
五:演示和使用
在 vue template 中添加結(jié)構(gòu)代碼
<Validate-Form @form-submit="submitForm" ref="validateFormRef"> <!-- 郵箱地址 --> <div class="mb-3"> <label for="exampleInputEmail1" class="form-label">郵箱地址</label> <!-- 自定義組件Validate-input --> <Validate-input v-model="loginParams.email" :rules="emailRules" placeholder="請(qǐng)輸入郵箱地址"></Validate-input> </div> <!-- 密碼 --> <div class="mb-3"> <label for="exampleInputPassword1" class="form-label">密碼</label> <Validate-input v-model="loginParams.password" :rules="passwordRules" type="password" placeholder="請(qǐng)輸入密碼" ></Validate-input> </div> <template #submit> <button type="submit" class="btn btn-primary btn-block btn-large">提交</button> </template> </Validate-Form>
在 setup語法糖中添加數(shù)據(jù)
//定義驗(yàn)證類型數(shù)據(jù) const emailRules: RulesProp = [ { type: 'required', message: '電子郵箱地址不能為空' }, { type: 'email', message: '請(qǐng)輸入正確的電子郵箱格式' } ] //定義密碼驗(yàn)證類型數(shù)據(jù) const passwordRules: RulesProp = [ { type: 'required', message: '密碼不能為空' }, { type: 'range', min: { message: '你的密碼至少包括6位,不能含有空格', length: 6 }, max: { message: '你的密碼至多15位,不能含有空格', length: 15 } } ] //監(jiān)聽結(jié)果 const submitForm = (result: boolean) => { console.log(result) }
Validate-Form屬性和事件
form-submit:
類型:事件
默認(rèn):-
說明:回調(diào)參數(shù) (result: boolean) => void, result 代表是否通過了驗(yàn)證
Validate-Input屬性和事件
rules
- 類型:array
- 默認(rèn):-
- 說明: 單個(gè)輸入框的驗(yàn)證類型,可以傳入包含特定對(duì)象的數(shù)組, 詳情可見上面示例代碼
tag
- 類型:input | textarea
- 默認(rèn):input
- 說明: 根節(jié)點(diǎn)類型
v-model
- 類型: string
- 默認(rèn):-
- 說明: 支持 v-model,請(qǐng)對(duì)響應(yīng)式數(shù)據(jù)
到此這篇關(guān)于Vue Validate表單組件的封裝詳解的文章就介紹到這了,更多相關(guān)Vue Validate內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue純前端使用exceljs導(dǎo)出excel文件方式
本文介紹了如何使用excel.js插件和Vue實(shí)現(xiàn)純前端Excel文件導(dǎo)出,并詳細(xì)步驟包括安裝插件、創(chuàng)建工作簿和工作表、定義列數(shù)據(jù)、設(shè)置樣式、行級(jí)操作、遍歷行和單元格、插入圖片和超鏈接2024-11-11element-ui復(fù)雜table表格動(dòng)態(tài)新增列、動(dòng)態(tài)調(diào)整行以及列順序詳解
這篇文章主要給大家介紹了關(guān)于element-ui復(fù)雜table表格動(dòng)態(tài)新增列、動(dòng)態(tài)調(diào)整行以及列順序的相關(guān)資料,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08vue子組件實(shí)時(shí)獲取父組件的數(shù)據(jù)實(shí)現(xiàn)
本文主要介紹了vue子組件實(shí)時(shí)獲取父組件的數(shù)據(jù)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12Vue.js動(dòng)態(tài)添加、刪除選題的實(shí)例代碼
這篇文章主要介紹了Vue.js動(dòng)態(tài)添加、刪除選題的實(shí)例代碼,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09vue中改變了vuex數(shù)據(jù)視圖不更新,也監(jiān)聽不到的原因及解決
這篇文章主要介紹了vue中改變了vuex數(shù)據(jù)視圖不更新,也監(jiān)聽不到的原因及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03