日常业务中,form表单很常见了,在使用Vue开发时,使用ElementUI作为组件开发使用;除了UI组件提供的通用form组件,如:input、select等等 , 由于业务的不同,不仅仅局限于简单的输入、选择 . 会存在组合操作,需要按照业务逻辑处理输入/选择的值 . 探究各UI组件的实现 , 并将自定义form组件融入到form表单中 , 也很重要.
示例中各框架、组件版本
Vue@2.6
Element@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;
},
示例中定义的model
prop 同 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", // 定义不同 的prop
event:"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 ; 初始化校验规则 , 实例对象 validator
const model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, (errors, invalidFields) => { // 调用validat 方法, 校验给定的值model
this.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>
测试,搞定! :white_check_mark:
本来还想继续写v-model
源码实现,看的好累, 东西太多了,看的头大;先不加了 :stuck_out_tongue_winking_eye:
今天的文章Vue 系列 – v-model自定义组件结合el-form做表单校验分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17059.html