• el-table通过这样封装可以实现校验-表格校验的原理


    我们一般在后台系统中,很常见的操作时表格里面嵌套表单,之前我的网上找到了一些封装的用法:

    <el-form :model="formData" :rules="ruleData" ref="formDom">
      <el-table :data="formData.tableData">
        <el-table-column
          v-for="item in column"
          :key="item.prop"
          :label="item.label"
        >
          <template slot-scope="scope">
            <el-form-item
              :ref="'tableData.' + scope.$index + '.' + item.prop"
              :prop="'tableData.' + scope.$index + '.' + item.prop"
              :rules="ruleData[item.prop]"
            >
              <el-input
                v-model="scope.row[item.prop]"
                @change="handleChange(scope, item)"
              ></el-input>
            </el-form-item>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    
    // data中的数据
    formData: {
      tableData: [
        { name: "", age: "" },
        { name: "", age: "" },
        { name: "", age: "" },
        { name: "", age: "" },
      ],
    },
    ruleData: {
      name: { message: "请输入名字", required: true },
      age: { message: "请输入年龄", required: true },
    },
    column: [
      { label: "名字", prop: "name" },
      { label: "年龄", prop: "age" },
    ],
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40

    在这里我不太理解prop为什么要写成"'tableData.' + scope.$index + '.' + item.prop"这个样子就实现了校验的效果。后来在一次项目中偶然去查看了一下el-form的源码,才明白其中的道理;
    在这里插入图片描述
    首先我们看到el-form组件的文件路径是这样的,那么其实form-item是一个单独的组件,我们通过form -> form-item嵌套的时候,其实最后实现校验的过程是一个个去校验 form-item,form组件最终的校验:

    // 最主要的核心功能
    validate(callback){
      this.fields.forEach(field => {
      field.validate('', (message, field) => {
        if (message) {
          valid = false;
        }
        invalidFields = objectAssign({}, invalidFields, field);
        if (typeof callback === 'function' && ++count === this.fields.length) {
          callback(valid, invalidFields);
        }
      });
    });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    其中 this.fields就是el-form-item集合:

    // el-form的created中
    this.$on('el.form.addField', (field) => {
      if (field) {
        this.fields.push(field);
      }
    });
    
    // el-form-item的mounted中
    this.dispatch('ElForm', 'el.form.addField', [this]);
    
    // 内部自己使用 dispatch实现组件通讯
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其中最主要的就是调用el-form-itemvalidate方法:

    validate(trigger, callback = noop) {
            this.validateDisabled = false;
            const rules = this.getFilteredRule(trigger);
            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);
            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

    其中主要的核心功能是单个的 rules(校验规则)model(数据)
    rules的获取:

    // 首先通过getFilteredRule方法过滤rules
    getFilteredRule(trigger) {
      // 获取 rules
      const rules = this.getRules();
      return rules.filter(rule => {
        if (!rule.trigger || trigger === '') return true;
        if (Array.isArray(rule.trigger)) {
          return rule.trigger.indexOf(trigger) > -1;
        } else {
          return rule.trigger === trigger;
        }
      }).map(rule => objectAssign({}, rule));
    },
    
    // getFilteredRule最核心的方式就是getRules
    getRules() {
      // 这个就是我们在 el-form中传递的rules
      let formRules = this.form.rules;
      // 这个就是我们自己在 el-form-item传递的rules
      const selfRules = this.rules;
      // 这里判断是不是必输的
      const requiredRule = this.required !== undefined ? { required: !!this.required } : [];
      // 这里通过formRules结合prop获取最新的prop(其实是一个对象,里面有key,value比较重要的值)
      const prop = getPropByPath(formRules, this.prop || '');
      formRules = formRules ? (prop.o[this.prop || ''] || prop.v) : [];
      // 最终将rules做一个整合
      return [].concat(selfRules || formRules || []).concat(requiredRule);
    },
    
    • 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

    最终我们通过分析发现其实去匹配 el-form中整体的rules是通过getPropByPath这个方法的:

    function getPropByPath(obj, path) {
      let tempObj = obj;
      path = path.replace(/\[(\w+)\]/g, '.$1');
      path = path.replace(/^\./, '');
      let keyArr = path.split('.');
      let i = 0;
      for (let len = keyArr.length; i < len - 1; ++i) {
        let key = keyArr[i];
        if (key in tempObj) {
          tempObj = tempObj[key];
        } else {
          throw new Error('please transfer a valid prop path to form item!');
        }
      }
      return {
        o: tempObj,
        k: keyArr[i],
        v: tempObj[keyArr[i]]
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    path.split('.')这里会对prop进行切割,我们最终得到的值其实是这样的:
    在这里插入图片描述
    最终通过formRules = formRules ? (prop.o[this.prop || ''] || prop.v) : [];formRules这个值变成了undefined
    再返回validate方法里面,看model,通过这里model[this.prop] = this.fieldValue,我们来看fieldValue

    function getPropByPath(obj, path) {
      // 这里的值其实一个对象数组:
      // { tableData: [
      // { name: "", age: "" },
      // { name: "", age: "" },
      // { name: "", age: "" },
      // { name: "", age: "" },
    // ] },
      let tempObj = obj;
      path = path.replace(/\[(\w+)\]/g, '.$1');
      path = path.replace(/^\./, '');
      // 那第一个来说就是 tableData.0.name
      let keyArr = path.split('.');
      let i = 0;
      for (let len = keyArr.length; i < len - 1; ++i) {
        // 不断的去匹配 tempObj 中的值
        let key = keyArr[i];
        if (key in tempObj) {
          tempObj = tempObj[key];
        } else {
          throw new Error('please transfer a valid prop path to form item!');
        }
      }
      return {
        o: tempObj,
        k: keyArr[i],
        v: tempObj[keyArr[i]]
      };
    }
    // 其实就是一个计算属性
    fieldValue() {
      // 我们给 el-form传递的model值
      const model = this.form.model;
      if (!model || !this.prop) { return; }
      let path = this.prop;
      if (path.indexOf(':') !== -1) {
        path = path.replace(/:/, '.');
      }
      // 还是通过这个方法获取到对应的值
      return getPropByPath(model, path, true).v;
    },
    
    • 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
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    最终fieldValue就表示了表格组件中每个小块的值,通过 propmodel去匹配对应的值,和rules结合AsyncValidator实现表单校验。这里我们也可以看出给el-form传递model,给el-form-item传递proprules的重要性。
    写到这里我还有一个疑问,那就是在getRules方法中,通过getPropByPath其实最终是把formRules转换成一个undefined的,那么是不是在此次封装中给el-form传递的rules没用了,其实真正起主导作用的还是给el-form-item传递的rules,那么把el-form-item中的rules删除掉是不是就校验不了了。通过验证证实了我的猜想。

  • 相关阅读:
    基于Java的在线考试系统(附:源码和课件)
    「数据结构详解·七」并查集的初步
    从Python语言的角度看C++的指针
    【机器学习】什么是随机马尔科夫决策过程?
    postgresql14管理(五)-tablespace
    16 JavaScript学习: 类型转换
    数据结构名词解释详细总结
    矩阵分析与应用-6.2-奇异值分解-Section1
    Docker部署Jumpserver堡垒机
    深入理解Linux文件描述符
  • 原文地址:https://blog.csdn.net/m0_47531829/article/details/136355366