vue表單驗(yàn)證你真的會(huì)了嗎?vue表單驗(yàn)證(form)validate
前言
很久沒有寫文章了,學(xué)習(xí)了一下webpack,基礎(chǔ)的一些組件,今天帶來form表單驗(yàn)證組件(element.iviewui)的一期教程(作為一個(gè)菜雞畢竟經(jīng)歷眾多項(xiàng)目可以給一些新手一點(diǎn)提示 (QQ群技術(shù)討論)838293023備注(github進(jìn)來的
github 技術(shù)文檔 技術(shù)文檔會(huì)持續(xù)更新
效果圖
1.原理解釋
考慮
我們看一下我們可以用form去整體觸發(fā)校驗(yàn)也可以單個(gè)input來觸發(fā)form-item 進(jìn)行校驗(yàn) 童鞋們現(xiàn)在可能感覺還是沒懂,沒關(guān)系繼續(xù)往下看。
2.派發(fā)和廣播
為什么要用廣播和派發(fā)呢。通常我們和業(yè)務(wù)沒有關(guān)系的組件盡量不要使用vuex和bus(事件總線)。 下面我送上廣播和派發(fā)的代碼。我們?cè)谛枰{(diào)用組件綁上 this.$on('event',res=>())
,通過派發(fā)和廣播進(jìn)行調(diào)用 $emit
。
- 派發(fā)是向上查找且只調(diào)用1個(gè)
- 廣播是向下查找調(diào)用多個(gè)
- 注意⚠️所有的組件都要寫上name
- 通過混合器 mixins 來使用
emitter.js /** * 遞歸使用 call 方式this指向 * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數(shù) */ function broadcast(componentName, eventName, params) { // 循環(huán)子節(jié)點(diǎn)找到名稱一樣的子節(jié)點(diǎn) 否則 遞歸 當(dāng)前子節(jié)點(diǎn) this.$children.map(child=>{ if (componentName===child.$options.name) { child.$emit.apply(child,[eventName].concat(params)) }else { broadcast.apply(child,[componentName,eventName].concat(params)) } }) } export default { methods: { /** * 派發(fā) (向上查找) (一個(gè)) * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數(shù) */ dispatch(componentName, eventName, params) { let parent = this.$parent || this.$root;//$parent 找到最近的父節(jié)點(diǎn) $root 根節(jié)點(diǎn) let name = parent.$options.name; // 獲取當(dāng)前組件實(shí)例的name // 如果當(dāng)前有節(jié)點(diǎn) && 當(dāng)前沒名稱 且 當(dāng)前名稱等于需要傳進(jìn)來的名稱的時(shí)候就去查找當(dāng)前的節(jié)點(diǎn) // 循環(huán)出當(dāng)前名稱的一樣的組件實(shí)例 while (parent && (!name||name!==componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.name; } } // 有節(jié)點(diǎn)表示當(dāng)前找到了name一樣的實(shí)例 if (parent) { parent.$emit.apply(parent,[eventName].concat(params)) } }, /** * 廣播 (向下查找) (廣播多個(gè)) * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數(shù) */ broadcast(componentName, eventName, params) { broadcast.call(this,componentName, eventName, params) } } }
3.async-validator
不懂 async-validator
可以去官網(wǎng)看看 github
yarn add async-validator // 因?yàn)楫?dāng)前這個(gè)插件是需要打包到項(xiàng)目里的所以不能加-D
4.api設(shè)計(jì)
我們看一下下面 element
官網(wǎng)的圖`
form
有2個(gè)注入的字段 :rules
規(guī)則,和 :model
當(dāng)前form的值會(huì)通過 model
的值和 rules
進(jìn)行匹配來進(jìn)行校驗(yàn).
form-item
有2個(gè)注入的字段 lable
和 prop
( prop
)是來和 form
進(jìn)行匹配來獲取當(dāng)前的 form-item
的值的
input
其實(shí)有當(dāng)前的 @input
的方法。 v-model
就不解釋了
form
我們?cè)?form
先開始注入當(dāng)前所有的 form-item
實(shí)例(獲?。?/p>
created
會(huì)在生命周期開始的時(shí)候綁定和刪除當(dāng)前實(shí)例的方法。通常綁定都在頁面dom開始前調(diào)用需要在 dom
加載完
provide
配合inject 使用讓子組件可以調(diào)用當(dāng)前父組件的方法以及data
下面都寫了備注可以放心食用(經(jīng)過測(cè)試當(dāng)前是可以進(jìn)行校驗(yàn)的)
form.vue <template> <form> <slot></slot> </form> </template> <script> export default { name: "aiForm", provide(){ // [不懂的可以看看](https://cn.vuejs.org/v2/api/#provide-inject) return { form: this } }, props: { // 當(dāng)前 form 的model model: { type: Object }, // 驗(yàn)證 rules: { type: Object } }, data(){ return{ fields: [] // 儲(chǔ)存當(dāng)前的 form-item的實(shí)例 } }, created(){ // 存當(dāng)前實(shí)例 let that =this; this.$on('on-form-item-add',item=>{ if (item) { that.fields.push(item) } }); // 刪除當(dāng)前有的實(shí)例 this.$on('on-form-item-remove',item=>{ if (item.prop) {// 如果當(dāng)前沒有prop的話表示當(dāng)前不要進(jìn)行刪除(因?yàn)闆]有注入) that.fields.splice(that.fields.indexOf(item),1) } }) }, methods:{ /** * 清空 */ resetFields(){//添加resetFields方法使用的時(shí)候調(diào)用即可 /** * 當(dāng)前所有當(dāng)form-item 進(jìn)行賦值 */ this.fields.forEach(field => { field.resetField(); }); }, /** * 校驗(yàn) 公開方法:全部校驗(yàn)數(shù)據(jù),支持 Promise */ validate(callback){ return new Promise(resolve=>{ /** * 當(dāng)前所有當(dāng)form-item 進(jìn)行校驗(yàn) */ let valid = true; // 默認(rèn)是通過 let count = 0; // 來匹配當(dāng)前是否是全部檢查完 this.fields.forEach(field => { // 每個(gè)實(shí)例都會(huì)有 validation 的校驗(yàn)的方法 field.validation('',error=>{ // 只要有一個(gè)不符合那么當(dāng)前的校驗(yàn)就是未通過的 if (error) { valid = false; } // 通過當(dāng)前檢查完所有的form-item的時(shí)候才會(huì)調(diào)用 if (++count === this.fields.length) { resolve(valid);// 方法使用then if (typeof callback === 'function') { callback(valid);// 直接調(diào)用注入的回調(diào)方法 } } }); }); }) } } } </script>
5.form-item
- form-item比較復(fù)雜我們一個(gè)一個(gè)講
- isRequired來判斷當(dāng)前是否需要必填
- validateState來判斷當(dāng)前校驗(yàn)的狀態(tài)
- validateMessage當(dāng)前的錯(cuò)誤的值
- inject: ['form'] 我們就可以通過this.from.xxx來調(diào)用父組件的事件以及值了
- computed下的fieldValue可能在不停的變化所以我們通過計(jì)算屬性來使用
- initialValue 默認(rèn)的值我們?cè)趍ounted的時(shí)候且當(dāng)前需要進(jìn)行校驗(yàn)的時(shí)候(prop有的時(shí)候)會(huì)賦值
- mixins: [Emitter]混合器就是里面的方法以及date都可以在當(dāng)前調(diào)用使用頻繁的都可以放在混合器里面
- 我們form-item 會(huì)傳入input的兩個(gè)方法blur和change(input原生使用的@input)通過form傳入的校驗(yàn)rules里面的trigger來判斷
form-item.vue <template> <div> <label :class="isRequired?'ai-form-item-label-required':''">{{label}}</label> <div> <slot></slot> <div class="ai-form-item-message" v-if="validateState==='error'">{{validateMessage}}</div> </div> </div> </template> <script> import Emitter from '../../mixins/emitter'; import schema from 'async-validator'; export default { name: "aiFormItem", mixins: [Emitter], inject: ['form'], props: { label: { type: String, default: '' }, prop:{ type: String }, }, computed:{ fieldValue () { return this.form.model[this.prop]; }, }, data(){ return { initialValue: '', // 儲(chǔ)存默認(rèn)值 isRequired: false, // 當(dāng)前的是否有問題 validateState: '', // 是否校驗(yàn)成功 validateMessage: '', // 校驗(yàn)失敗文案 } }, methods:{ /** * 綁定事件 進(jìn)行是否 required 校驗(yàn) */ setRules(){ let that = this; let rules = this.getRules();//拿到父組件過濾后當(dāng)前需要使用的規(guī)則 if (rules.length) { // every 方法用于檢測(cè)數(shù)組所有元素是否都符合指定條件(通過函數(shù)提供) // some 只要有一個(gè)符合就返回true this.isRequired = rules.some(rule=>{ // 如果當(dāng)前校驗(yàn)規(guī)則中有必填項(xiàng),則標(biāo)記出來 return rule.required; }) } /** * blur 事件 */ this.$on('on-form-blur',that.onFieldBlur); /** * change 事件 */ this.$on('on-form-change',that.onFieldChange) }, /** * 從 Form 的 rules 屬性中,獲取當(dāng)前 FormItem 的校驗(yàn)規(guī)則 */ getRules () { let that = this; let rules = that.form.rules; rules = rules?rules[that.prop]:[]; return [].concat(rules||[])//這種寫法可以讓規(guī)則肯定是一個(gè)數(shù)組的形式 }, /** * Blur 進(jìn)行表單驗(yàn)證 */ onFieldBlur(){ this.validation('blur') }, /** * change 進(jìn)行表單驗(yàn)證 */ onFieldChange(){ this.validation('change') }, /** * 只支持 blur 和 change,所以過濾出符合要求的 rule 規(guī)則 */ getFilteredRule (trigger) { let rules = this.getRules(); // !res.trigger 沒有調(diào)用方式的時(shí)候默認(rèn)就校驗(yàn)的 // filter 過濾出當(dāng)前需要的規(guī)則 return rules.filter(res=>!res.trigger || res.trigger.indexOf(trigger)!==-1) }, /** * 校驗(yàn)數(shù)據(jù) * @param trigger 校驗(yàn)類型 * @param callback 回調(diào)函數(shù) */ validation(trigger,callback=function () {}){ // blur 和 change 是否有當(dāng)前方式的規(guī)則 let rules = this.getFilteredRule(trigger); // 判斷當(dāng)前是否有規(guī)則 if (!rules || rules.length === 0) { return } // 設(shè)置狀態(tài)為校驗(yàn)中 // async-validator的使用形式 this.validateState = 'validating'; var validator = new schema({[this.prop]: rules}); // firstFields: true 只會(huì)校驗(yàn)一個(gè) validator.validate({[this.prop]: this.fieldValue}, { firstFields: true },(errors, fields) => { this.validateState = !errors ? 'success' : 'error'; this.validateMessage = errors ? errors[0].message : ''; callback(this.validateMessage); }); }, /** * 清空當(dāng)前的 form-item */ resetField(){ this.form.model[this.prop] = this.initialValue; } }, // 組件渲染時(shí),將實(shí)例緩存在 Form 中 mounted(){ // 如果沒有傳入 prop,則無需校驗(yàn),也就無需緩存 if (this.prop) { this.dispatch('aiForm','on-form-item-add', this); // 設(shè)置初始值,以便在重置時(shí)恢復(fù)默認(rèn)值 this.initialValue = this.fieldValue; // 添加表單校驗(yàn) this.setRules() } }, // 組件銷毀前,將實(shí)例從 Form 的緩存中移除 beforeDestroy(){ this.dispatch('iForm', 'on-form-item-remove', this); }, } </script> <style scoped> <!--當(dāng)前css--> .ai-form-item-label-required:before{ content: '*'; color: red; } .ai-form-item-message { color: red; } </style>
5.input
- value 支持一個(gè)入?yún)?/li>
- 因?yàn)楫?dāng)前是一個(gè) input 注入的參數(shù)是不能直接放到 input 里面使用的所以先賦值給了 defaultValue 然后用 watch 來不停給 defaultValue 賦值達(dá)到一個(gè)父組件修改后的一個(gè)綁定
<template> <input type="text" @input="handleInput" // change @blur="handleBlur" :value="defaultValue" > </template> <script> import Emitter from '../../mixins/emitter.js' export default { name: "aiInput", mixins: [Emitter], props: { value: { type: String, default: '' } }, data(){ return { defaultValue: this.value } }, watch:{ value (val) { this.defaultValue = val; } }, methods:{ /** * change 事件 * @param event */ handleInput(event){ // 當(dāng)前model 賦值 this.defaultValue = event.target.value; // vue 原生的方法 return 出去 this.$emit('input',event.target.value); // 將當(dāng)前的值發(fā)送到 aiFormItem 進(jìn)行校驗(yàn) this.dispatch('aiFormItem','on-form-change',event.target.value) }, /** * blur 事件 * @param event */ handleBlur(event){ // vue 原生的方法 return 出去 this.$emit('blur',event.target.value); // 將當(dāng)前的值發(fā)送到 aiFormItem 進(jìn)行校驗(yàn) this.dispatch('aiFormItem','on-form-blur',event.target.value) } } } </script>
最后
最后給上一個(gè)當(dāng)前可以的使用方式
<template> <div class="home"> <button @click="changeButton">測(cè)試</button> <ai-form ref="formItems" :model="formValidate" :rules="ruleValidate"> <ai-form-item label="用戶名" prop="name"> <ai-input v-model="formValidate.name"/> </ai-form-item> </ai-form> </div> </template> <script> import AiForm from "../components/form/form"; import AiFormItem from "../components/form/form-item"; import AiInput from "../components/input/ai-input"; export default { name: 'home', components: {AiInput, AiFormItem, AiForm},], data(){ return{ formValidate: { name: '123z', mail: '' }, ruleValidate: { name: [ { required: true, message: '用戶名不能為空', trigger: 'blur' }, ], } } }, methods:{ changeButton(){ this.$refs.formItems.resetFields() // 清空方法 this.$refs.formItems.validate() // 驗(yàn)證方法 .then(res=>{ console.log(res) }) } }, } </script>
小結(jié)
可能現(xiàn)在小伙伴還是不懂。。俗話說;師傅領(lǐng)進(jìn)門,修行在個(gè)人。代碼上的備注寫的也夠多了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue中EpicEditor和vue-quill-editor的使用詳解
EpicEditor和Vue-quill-editor都是基于Quill.js的富文本編輯器,并且都提供了許多強(qiáng)大的功能,下面我們就來介紹一下二者的具體使用,感興趣的小伙伴可以了解一下2023-11-11項(xiàng)目中Axios二次封裝實(shí)例Demo
vue項(xiàng)目經(jīng)常會(huì)用到axios來請(qǐng)求數(shù)據(jù),那么首先肯定需要對(duì)這個(gè)請(qǐng)求方法進(jìn)行一個(gè)二次封裝,這篇文章主要給大家介紹了關(guān)于項(xiàng)目中Axios二次封裝的相關(guān)資料,需要的朋友可以參考下2021-06-06vue如何使用swiper插件修改左右箭頭的默認(rèn)樣式
這篇文章主要介紹了vue如何使用swiper插件修改左右箭頭的默認(rèn)樣式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01vue+elementUI組件table實(shí)現(xiàn)前端分頁功能
這篇文章主要為大家詳細(xì)介紹了vue+elementUI組件table實(shí)現(xiàn)前端分頁功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12element ui里dialog關(guān)閉后清除驗(yàn)證條件方法
下面小編就為大家分享一篇element ui里dialog關(guān)閉后清除驗(yàn)證條件方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-02-02JavaScript獲取echart曲線上任意點(diǎn)位的值詳解
這篇文章主要為大家介紹了JavaScript獲取echart曲線上任意點(diǎn)位的值詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09