• 深入了解Element Form表单动态验证问题 转载


     

    在上一篇《vue elementUI组件表单动态验证失效的问题与解决办法》中,讲到直接修改prop属性,未触发form-item的重新渲染,所以虽然有校验*的标志,实际上并不会校验。这是表面现象,最近有了空余时间,去看看了element form组件的源码,找到了根本原因。

    源码分析

    1. form组件的created钩子函数中添加了el.form.addFieldel.form.removeField事件监听,往fields中添加或删除field,校验的时候会遍历fields数组,而field就是form-item的实例。接着查看form-item组件的源码,可以看到在组件挂载后mounted,如果prop属性有值就会触发el.form.addField事件,在组件销毁前beforeDestroy触发el.form.removeField事件。由此可知如果挂载时form-item组件prop属性无值,不会触发el.form.addField
    // form.vue
    created() {
      this.$on('el.form.addField', (field) => {
        if (field) {
          this.fields.push(field);
        }
      });
      /* istanbul ignore next */
      this.$on('el.form.removeField', (field) => {
        if (field.prop) {
          this.fields.splice(this.fields.indexOf(field), 1);
        }
      });
    }
    
    // form-item.vue
    mounted() {
      // 重点挂载前有prop属性,才会触发'el.form.addField'
      if (this.prop) {
        this.dispatch('ElForm', 'el.form.addField', [this]);
        // ...
        this.addValidateEvents();
      }
    },
    beforeDestroy() {
      this.dispatch('ElForm', 'el.form.removeField', [this]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. form表单校验是调用表单实例的validate方法,去掉边界、合法性判断一类的代码,其核心源码如下,从代码注释中可以知道,form表单的校验方法核心是调用form-item组件实例的validate方法。
    // form.vue
    methods: {
      validate(callback) {
        // ...
        this.fields.forEach(field => {
          // 调用form-item实例的validate方法
          field.validate('', (message, field) => {
            if (message) {
              valid = false;
            }
            invalidFields = objectAssign({}, invalidFields, field);
            if (typeof callback === 'function' && ++count === this.fields.length) {
              callback(valid, invalidFields);
            }
          });
        });
      }
    },
    
    watch: {
      // 如果rules有变化,强制更新form-item的校验事件,并触发一次form实例validate方法
      rules() {
        // remove then add event listeners on form-item after form rules change
        this.fields.forEach(field => {
          field.removeValidateEvents();
          field.addValidateEvents();
        });
    
        if (this.validateOnRuleChange) {
          this.validate(() => {});
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    1. 最后看form-itemvalidate方法,省略一些逻辑判断和优化代码,其核心是获取form组件的对应属性的rulesform-item自身的rules,生成AsyncValidator校验器的校验规则描述,并进行数据校验。
    // form-item.vue
    validate(trigger, callback = noop) {
      this.validateDisabled = false;
      // 获取form组件的对应属性的rules及form-item自身的rules,并根据触发器trigger过滤
      const rules = this.getFilteredRule(trigger);
      // 判断校验规则,假值或者rules数组为空,则跳过。值得注意的是空对象并不是假值,所以不会跳过
      if ((!rules || rules.length === 0) && this.required === undefined) {
        callback();
        return true;
      }
    
      this.validateState = 'validating';
    
      // 设置AsyncValidator的校验规则描述
      const descriptor = {};
      if (rules && rules.length > 0) {
        rules.forEach(rule => {
          delete rule.trigger;
        });
      }
      descriptor[this.prop] = rules;
    
      const validator = new AsyncValidator(descriptor);
      const model = {};
    
      model[this.prop] = this.fieldValue;
    
      validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
        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);
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    从上面的第一点中可以看到,因为prop初始值为空字符串'',所以form表单的fileds不会持有该form-item实例,故即使后面修改prop属性,也不会去校验该表单项,导致校验失效。

    解决办法

    1. 《vue elementUI组件表单动态验证失效的问题与解决办法》中讲的强制重新渲染form-item,当时不知道根本原因采用的笨办法。
    2. form-item保留prop属性,根据条件动态修改form组件的rules对象。比如下面这样:
    watch: {
      'formData.age': {
        immediate: true,
        handler (newVal) {
          if (newVal >= 18) {
            this.addRule('bankCardNo', [{ required: true, message: '请输入银行卡号', trigger: 'blur' }])
          } else {
            this.addRule('bankCardNo', [])
          }
        }
      }
    }
    
    data () {
      return {
        rules: {}
      }
    },
    
    methods: {
      addRule (prop, rule) {
        this.$set(this.rules, prop, rule)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. form-item保留prop属性,根据条件动态修改form-item组件的rules属性。比如下面两种方式,因为rules属性接受数据和对象。
    <el-form-item label="银行卡号" prop="bankCardNo" :rules="formData.age >= 18 ? [{ required: true, message: '请输入银行卡号', trigger: 'blur' }] : []">
      <el-input v-model="formData.bankCardNo"></el-input>
    </el-form-item>
    
    <!-- 或者 -->
    <el-form-item label="银行卡号" prop="bankCardNo" :rules="formData.age >= 18 ? { required: true, message: '请输入银行卡号', trigger: 'blur' } : []">
      <el-input v-model="formData.bankCardNo"></el-input>
    </el-form-item>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    总结

    终须由浅入深,浮于表面便是管中窥豹,自身还需静下心认真研究

  • 相关阅读:
    JS 学习笔记
    Input控件只允许输入指定字符
    NPOI导出excel使用
    combobox级联检索下拉选择框
    vue父组件调用子组件方法
    EasyUI设置Layout自适应浏览器宽度和高度
    EasyUI创建选项卡并判断是否打开
    Jquery+ajaxfileupload上传文件
    Jquery禁用网页右键菜单
    c#删除指定文件夹中今天之前的文件
  • 原文地址:https://www.cnblogs.com/guiyishanren/p/14096590.html
Copyright © 2020-2023  润新知