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

Vue 系列 – v-model自定义组件结合el-form做表单校验1. ElementUI form表单基本使用 , 表单校验流程. 2. v-model 自定义组件 , 并结合element from进行校验.

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

示例中各框架、组件版本

Vue@2.6
Element@2.14.1

阅读本文你可以了解到:

  1. ElementUI form表单基本使用 , 表单校验流程.
  2. 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:

image-20210408205641948.png

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

今天的文章Vue 系列 – v-model自定义组件结合el-form做表单校验分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17059.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注