前言
很久沒有寫文章了,學習了一下webpack,基礎的一些組件,今天帶來form表單驗證組件(element.iviewui)的一期教程(作為一個菜雞畢竟經歷眾多項目可以給一些新手一點提示 (QQ群技術討論)838293023備注(github進來的
效果圖
1.原理解釋

考慮
我們看一下我們可以用form去整體觸發校驗也可以單個input來觸發form-item 進行校驗 童鞋們現在可能感覺還是沒懂,沒關系繼續往下看。
2.派發和廣播
為什么要用廣播和派發呢。通常我們和業務沒有關系的組件盡量不要使用vuex和bus(事件總線)。 下面我送上廣播和派發的代碼。我們在需要調用組件綁上 this.$on('event',res=>()) ,通過派發和廣播進行調用 $emit 。
emitter.js/** * 遞歸使用 call 方式this指向 * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數 */function broadcast(componentName, eventName, params) { // 循環子節點找到名稱一樣的子節點 否則 遞歸 當前子節點 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: { /** * 派發 (向上查找) (一個) * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數 */ dispatch(componentName, eventName, params) { let parent = this.$parent || this.$root;//$parent 找到最近的父節點 $root 根節點 let name = parent.$options.name; // 獲取當前組件實例的name // 如果當前有節點 && 當前沒名稱 且 當前名稱等于需要傳進來的名稱的時候就去查找當前的節點 // 循環出當前名稱的一樣的組件實例 while (parent && (!name||name!==componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.name; } } // 有節點表示當前找到了name一樣的實例 if (parent) { parent.$emit.apply(parent,[eventName].concat(params)) } }, /** * 廣播 (向下查找) (廣播多個) * @param componentName // 需要找的組件的名稱 * @param eventName // 事件名稱 * @param params // 需要傳遞的參數 */ broadcast(componentName, eventName, params) { broadcast.call(this,componentName, eventName, params) } }}3.async-validator
不懂 async-validator 可以去官網看看 github
yarn add async-validator // 因為當前這個插件是需要打包到項目里的所以不能加-D
4.api設計
我們看一下下面 element 官網的圖`
form 有2個注入的字段 :rules 規則,和 :model 當前form的值會通過 model 的值和 rules 進行匹配來進行校驗.
form-item 有2個注入的字段 lable 和 prop ( prop )是來和 form 進行匹配來獲取當前的 form-item 的值的
input 其實有當前的 @input 的方法。 v-model 就不解釋了
form
我們在 form 先開始注入當前所有的 form-item 實例(獲取)
created 會在生命周期開始的時候綁定和刪除當前實例的方法。通常綁定都在頁面dom開始前調用需要在 dom 加載完
provide 配合inject 使用讓子組件可以調用當前父組件的方法以及data
下面都寫了備注可以放心食用(經過測試當前是可以進行校驗的)
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: { // 當前 form 的model model: { type: Object }, // 驗證 rules: { type: Object } }, data(){ return{ fields: [] // 儲存當前的 form-item的實例 } }, created(){ // 存當前實例 let that =this; this.$on('on-form-item-add',item=>{ if (item) { that.fields.push(item) } }); // 刪除當前有的實例 this.$on('on-form-item-remove',item=>{ if (item.prop) {// 如果當前沒有prop的話表示當前不要進行刪除(因為沒有注入) that.fields.splice(that.fields.indexOf(item),1) } }) }, methods:{ /** * 清空 */ resetFields(){//添加resetFields方法使用的時候調用即可 /** * 當前所有當form-item 進行賦值 */ this.fields.forEach(field => { field.resetField(); }); }, /** * 校驗 公開方法:全部校驗數據,支持 Promise */ validate(callback){ return new Promise(resolve=>{ /** * 當前所有當form-item 進行校驗 */ let valid = true; // 默認是通過 let count = 0; // 來匹配當前是否是全部檢查完 this.fields.forEach(field => { // 每個實例都會有 validation 的校驗的方法 field.validation('',error=>{ // 只要有一個不符合那么當前的校驗就是未通過的 if (error) { valid = false; } // 通過當前檢查完所有的form-item的時候才會調用 if (++count === this.fields.length) { resolve(valid);// 方法使用then if (typeof callback === 'function') { callback(valid);// 直接調用注入的回調方法 } } }); }); }) } } }</script>5.form-item
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: '', // 儲存默認值 isRequired: false, // 當前的是否有問題 validateState: '', // 是否校驗成功 validateMessage: '', // 校驗失敗文案 } }, methods:{ /** * 綁定事件 進行是否 required 校驗 */ setRules(){ let that = this; let rules = this.getRules();//拿到父組件過濾后當前需要使用的規則 if (rules.length) { // every 方法用于檢測數組所有元素是否都符合指定條件(通過函數提供) // some 只要有一個符合就返回true this.isRequired = rules.some(rule=>{ // 如果當前校驗規則中有必填項,則標記出來 return rule.required; }) } /** * blur 事件 */ this.$on('on-form-blur',that.onFieldBlur); /** * change 事件 */ this.$on('on-form-change',that.onFieldChange) }, /** * 從 Form 的 rules 屬性中,獲取當前 FormItem 的校驗規則 */ getRules () { let that = this; let rules = that.form.rules; rules = rules?rules[that.prop]:[]; return [].concat(rules||[])//這種寫法可以讓規則肯定是一個數組的形式 }, /** * Blur 進行表單驗證 */ onFieldBlur(){ this.validation('blur') }, /** * change 進行表單驗證 */ onFieldChange(){ this.validation('change') }, /** * 只支持 blur 和 change,所以過濾出符合要求的 rule 規則 */ getFilteredRule (trigger) { let rules = this.getRules(); // !res.trigger 沒有調用方式的時候默認就校驗的 // filter 過濾出當前需要的規則 return rules.filter(res=>!res.trigger || res.trigger.indexOf(trigger)!==-1) }, /** * 校驗數據 * @param trigger 校驗類型 * @param callback 回調函數 */ validation(trigger,callback=function () {}){ // blur 和 change 是否有當前方式的規則 let rules = this.getFilteredRule(trigger); // 判斷當前是否有規則 if (!rules || rules.length === 0) { return } // 設置狀態為校驗中 // async-validator的使用形式 this.validateState = 'validating'; var validator = new schema({[this.prop]: rules}); // firstFields: true 只會校驗一個 validator.validate({[this.prop]: this.fieldValue}, { firstFields: true },(errors, fields) => { this.validateState = !errors ? 'success' : 'error'; this.validateMessage = errors ? errors[0].message : ''; callback(this.validateMessage); }); }, /** * 清空當前的 form-item */ resetField(){ this.form.model[this.prop] = this.initialValue; } }, // 組件渲染時,將實例緩存在 Form 中 mounted(){ // 如果沒有傳入 prop,則無需校驗,也就無需緩存 if (this.prop) { this.dispatch('aiForm','on-form-item-add', this); // 設置初始值,以便在重置時恢復默認值 this.initialValue = this.fieldValue; // 添加表單校驗 this.setRules() } }, // 組件銷毀前,將實例從 Form 的緩存中移除 beforeDestroy(){ this.dispatch('iForm', 'on-form-item-remove', this); }, }</script><style scoped> <!--當前css--> .ai-form-item-label-required:before{ content: '*'; color: red; } .ai-form-item-message { color: red; }</style>5.input
<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){ // 當前model 賦值 this.defaultValue = event.target.value; // vue 原生的方法 return 出去 this.$emit('input',event.target.value); // 將當前的值發送到 aiFormItem 進行校驗 this.dispatch('aiFormItem','on-form-change',event.target.value) }, /** * blur 事件 * @param event */ handleBlur(event){ // vue 原生的方法 return 出去 this.$emit('blur',event.target.value); // 將當前的值發送到 aiFormItem 進行校驗 this.dispatch('aiFormItem','on-form-blur',event.target.value) } } }</script>最后
最后給上一個當前可以的使用方式
<template> <div class="home"> <button @click="changeButton">測試</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() // 驗證方法 .then(res=>{ console.log(res) }) } },}</script>小結
可能現在小伙伴還是不懂。。俗話說;師傅領進門,修行在個人。代碼上的備注寫的也夠多了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答