1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Vue 系列 - v-model自定义组件结合el-form做表单校验

Vue 系列 - v-model自定义组件结合el-form做表单校验

时间:2024-06-08 02:04:03

相关推荐

Vue 系列 - v-model自定义组件结合el-form做表单校验

日常业务中,form表单很常见了,在使用Vue开发时,使用ElementUI作为组件开发使用;除了UI组件提供的通用form组件,如:input、select等等 , 由于业务的不同,不仅仅局限于简单的输入、选择 . 会存在组合操作,需要按照业务逻辑处理输入/选择的值 . 探究各UI组件的实现 , 并将自定义form组件融入到form表单中 , 也很重要.

示例中各框架、组件版本

Vue@2.6Element@2.14.1

阅读本文你可以了解到:

ElementUI form表单基本使用 , 表单校验流程.v-model 自定义组件 , 并结合element from进行校验.

ElementUI

这是一个基础示例:

elementForm.vue

<template><div style="width:40%"><el-form ref="form" :model="userInfo" :rules="rules"><el-form-item label="姓名" prop="name"><el-input v-model="userInfo.name" placeholder="姓名" maxlength="15"></el-input></el-form-item><el-form-item label="年龄" prop="age"><el-input v-model="userInfo.age" placeholder="年龄" ></el-input></el-form-item><el-form-item label="性别" prop="gender"><el-radio-group v-model="userInfo.gender"><el-radio label="0">男</el-radio><el-radio label="1">女</el-radio></el-radio-group></el-form-item><el-form-item label="爱好" prop="hobby"><el-checkbox-group v-model="userInfo.hobby"><el-checkbox v-for="item in hobbies" :key="item" :label="item" name="hobby"></el-checkbox></el-checkbox-group><div><el-input v-model.trim="hobby" placeholder="自定义" @change="handleAddHooby" size="mini" style="width:140px;"></el-input></div></el-form-item><el-form-item label="生日" prop="birthday"><el-date-picker v-model="userInfo.birthday"></el-date-picker></el-form-item><el-form-item><el-button type="primary" @click="handleSubmitInfo">提交</el-button></el-form-item></el-form></div></template><script>export default {data(){function validateAge(rule,value,callback){try{if(value===''){callback('请输入年龄')}if(!value.match(/^\d+$/)){callback('请输入数字')}if(value*1 <0 || value*1 >150){callback('请输入合理的年龄值')}callback()}catch(err){console.log(err)callback('请输入年龄')}}return {hobby:"",hobbies:['篮球','读书','游戏','唱歌','跳舞'],userInfo:{name:'',age:'',gender:'1',hobby:[],birthday:'',},rules:{name:[{required:true,message:'请输入姓名'}],age:[{validator:validateAge,trigger:'change'}]}}},methods:{handleAddHooby(){if(this.hobbies.includes(this.hobby)){return}this.hobbies.push(this.hobby)this.hobby = ''},handleSubmitInfo(){console.log(this.userInfo)this.$refs.form.validate((error,errrorInfo)=>{if(error){return}})}}}</script>

关注点:

<el-form ref="form" \>绑定 , 可获取到form表单实例,调用方法,比如:validate 手动校验、resetFields 重置表单 等.<el-form-item prop="name" \>绑定 , 与form绑定的规则集rules 键值对应 , 校验时获取校验规则.<el-input v-model="userInfo.name" \>组件的输入控制 , v-model 指令的使用

v-model实现自定义组件

发现在使用组件el-input, 在没有使用v-model绑定值时,用户无法输入.

<el-input placeholder="姓名"></el-input>

在查看Element源码部分后,发现是在代码里做了控制.

setNativeInputValue() {const input = this.$refs.input; // this.$refs.input 源码为 this.getInput();if (!input) return;if (input.value === this.nativeInputValue) return;input.value = this.nativeInputValue;},

示例中定义的modelprop 同 props 的value属性值, 所以当你在父级使用该组件时, 同时使用了v-model和value ,那么你不会在该组件中获取到props的value值(v-model优先级高,value 被忽略); 只有仅在使用value时, 你在自定义组件中才可以获取到.

custom-input.vue

<template><div><p>这是一个自定义输入组件</p><input ref="input" v-model="inputValue" @input="handleInput"/></div></template><script>export default {data(){return {inputValue:'',}},model:{// 定义v-model如何去处理该组件 ,值属性定义、事件定义prop:"value",event:"custom"},props:{value:[String,Number], // 由于绑定的属性值与v-model定义的prop一致, 两者选其一, v-model优先, value会被忽略},mounted(){this.inputValue = this.value},methods:{/*** 处理输入, 如果不使用v-model则 父组件需要监听 cusmo 的事件,并更新 value 的值* 使用了 v-model, 则不用管了, v-model 根据model 定义的event事件类型, 监听事件, 进行值更新.**/handleInput(e){console.log(this.inputValue)this.$emit('custom', Math.random()*10);},},}</script>

自定义组件引用 , v-model 和value 同时绑定, value 会被忽略.

// ... <custom-input v-model="inputValue" value="admin" />// ...data(){return {inputValue:'',}}

当然 , 自定义分发的事件this.$emit('custom', Math.random()*10);, 我们也可以在父组件对其进行监听

// ... <custom-input v-model="inputValue" @custom='handleVModel' />// ...data(){return {inputValue:'',}},methods:{handleVModel(val){console.log("listener - vModel :",val)}}

自定义输入组件关注点:

model组件属性设置 , 定义v-model 指令如何处理当前组件:数据名称、事件名称;

默认 model 的值prop - value ; event - input; 示例中定义了事件event - custom

model:{prop:"value",event:"custom"}

可以自定义props\event名称处理具体的业务.

组件内数据变化,需要分发事件, 当前定义的组件触发的事件类型this.$emit('custom', event.target.value);

v-model会忽略所有表单元素绑定的默认值 , 比如:value\checked\selected,

v-model绑定的 model-prop 属性名称 会被传入组件的props ,需定义 props - value 拿到v-model的值

测试 - 定义不同model - prop 查看输入如何绑定 ; props 中的value 可用于初始化组件内部的 input 值;

// ... model:{// 定义v-model如何去处理该组件 ,值属性定义、事件定义prop:"customValue", // 定义不同 的propevent:"custom"},props:{customValue:[String,Number], // 双向绑定的 prop 值, 需要在该组件props定义value:[String,Number], },watch:{customValue(val){console.log(val)}},mthods:{/*** 内部的input输入框输入* 格式化内部输入的数据 , 将格式化的数据给v-model ,那父级绑定的就是给定的值**/handleInput(e){this.$emit('custom', Math.random()*10); // 此处定义的 v-model 响应的值给随机数, 区分和内部input的输入},}// ....

通过改造, 定义组件中不同的model-prop . 可以拿到value值来初始化组件内部的input值. v-model 定义的prop 需要定义在组件的props中, 可通过watch监听值更新打印查看.

思考: 自定义form输入组件中使用v-model时, ,v-model好比一个高阶组件 , 自身内部维护一个model-prop 名称的属性值, 监听model-event的事件名称的事件 . 事件触发更新自身的prop的属性的值. 值更新时,同时调用父级、自身的重新渲染.

el-form校验原理

当用户输入值或更改值时,是如何检测并触发校验的

通过查看源代码部分, 通过监听value的值,分发事件到父级

watch: {value(val) {// this.$nextTick(this.resizeTextarea);if (this.validateEvent) {this.dispatch('ElFormItem', 'el.form.change', [val]); // 转发事件到el-form-item ; dispatch 为内部自定义事件转发函数}},}

再看el-form-item是怎么处理该事件的 , 在mounted 后, 调用addValidateEvents, 监听了事件,并调用了addValidateEvents方法.

addValidateEvents() {const rules = this.getRules();if (rules.length || this.required !== undefined) {this.$on('el.form.blur', this.onFieldBlur);this.$on('el.form.change', this.onFieldChange); // 监听事件 , 触发对应的回调函数}},onFieldChange() {if (this.validateDisabled) {this.validateDisabled = false;return;}this.validate('change'); // 触发校验 , change 为校验规则中 trigger 的值},validate(trigger, callback = noop) {this.validateDisabled = false;const rules = this.getFilteredRule(trigger); // 获取校验规则, 绑定在el-form上的rules、el-form-item上的rules、required 合并if ((!rules || rules.length === 0) && this.required === undefined) {callback();return true;}this.validateState = 'validating';const descriptor = {};if (rules && rules.length > 0) {rules.forEach(rule => {delete rule.trigger;});}descriptor[this.prop] = rules; // 校验规则合集const validator = new AsyncValidator(descriptor); // 引用的库 async-validator ; 初始化校验规则 , 实例对象 validatorconst model = {};model[this.prop] = this.fieldValue; validator.validate(model, {firstFields: true }, (errors, invalidFields) => {// 调用validat 方法, 校验给定的值modelthis.validateState = !errors ? 'success' : 'error';this.validateMessage = errors ? errors[0].message : '';callback(this.validateMessage, invalidFields);this.elForm && this.elForm.$emit('validate', this.prop, !errors, this.validateMessage || null); // el-form 任意表单触发校验后的事件,包括当前属性名称、校验结果、校验信息 });},

自定义组件结合el-form进行校验

如果我们自定义的组件需要融入到el-form中,并统一校验规则出处 . 需要在自定义组件中在值发生更改后,分发事件出来.

需要改造我们之前自定义的组件custom-input.vue, 需要在v-model 值更改时,分发校验事件触发校验.

import emitter from 'element-ui/src/mixins/emitter'; // 分发事件的el-form处理方法 // ...mixins:[emitter], // 混入的方式 , 加载到当前组件watch:{customValue(val){console.log(val)this.dispatch('ElFormItem', 'el.form.change', [val]); // 值发生变化时, 向el-form-item分发事件 , 调用组件内部的校验流程}},

写完了,来引用到父级表单组件中测试一番. 看效果如何 , 使用如下:

<template><div style="width:40%"><el-form ref="form" :model="userInfo" :rules="rules"><!-- // ... 省略其他 --><el-form-item label="自定义" prop="randomValue"><custom-input v-model="userInfo.randomValue" /></el-form-item><el-form-item><el-button type="primary" @click="handleSubmitInfo">提交</el-button></el-form-item></el-form></div></template><script>import CustomInput from './custom-input'export default {data(){// ... function validateAgeRandomValue(rule,value,callback){// 对自定义组件的值进行自定义校验规则try{if(value===''){callback('请输入随机数')}if(value<3){callback('太小了')}if(value>7){callback('太大了')}callback()}catch(err){callback('出错了')}}return {customInputValue:"",hobby:"",hobbies:['篮球','读书','游戏','唱歌','跳舞'],userInfo:{name:'',age:'',gender:'1',hobby:[],birthday:'',randomValue:'',},rules:{name:[{required:true,message:'请输入姓名'}],age:[{validator:validateAge,trigger:'change'}],randomValue:[{required:true,message:'请输入一个随机值'},{validator:validateAgeRandomValue}], // 自定义规则}}},components:{CustomInput},// ...}</script>

测试,搞定! ✅

本来还想继续写v-model源码实现,看的好累, 东西太多了,看的头大;先不加了 😜

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。