草庐IT

element UI 组件中表单自定义校验规则如何传递参数?

淡然自若_blog 2023-08-14 原文

问题描述:在使用日期组件DatePicker时,需要自定义一个日期校验规则,为了后续的重复使用,需要将改校验方法抽成一个公用的校验方法,在抽象时需要几个自定义的参数用于辅助处理,那么这些参数该如何传递到自定义的校验方法中呢?

需求如下:

// 伪代码
function 自定义校验(test1, test2, callback) {
    if (value === 'test1') {
        callback()
        } else (value === 'test2') {
            callback()
        } else {
            callback()
        }
}

element官方的自定义校验规则实现方式如下,先写好自定义校验函数,然后直接将该函数赋值给相应的prop即可,官方实例

// 不相关的内容被删减掉了
<el-form  :rules="rules" ref="ruleForm" >
  <el-form-item label="年龄" prop="age">
    <el-input></el-input>
  </el-form-item>
</el-form>

<script>
  export default {
    data() {
      // 定义校验函数
      var checkAge = (rule, value, callback) => {
        // 以下是校验规则
        if (!value) {
          return callback(new Error('年龄不能为空'));
        }
        setTimeout(() => {
          if (!Number.isInteger(value)) {
            callback(new Error('请输入数字值'));
          } else {
            if (value < 18) {
              callback(new Error('必须年满18岁'));
            } else {
              callback();
            }
          }
        }, 1000);
      };
        
      return {
        rules: {
          age: [
             // 调用自定义校验函数
            { validator: checkAge, trigger: 'blur' }
          ]
        }
      };
    },
        
    methods: {
     ...
    }
  }
</script>

通过上述可以看到表单在掉用自定义校验方法时直接将校验函数赋值给validator,以一个校验规则的形式成为age数组中的一个对象,为了方便理解,这里成为校验规则对象{ validator: checkAge, trigger: ‘blur’ }(一个prop可以有多个校验规则对象,所以age是一个数组)

且上方校验函数的入参有三个(rule, value, callback)

新的思考:①这三个参数分别是什么;②校验函数的入参必须这么写吗?

解答①rule是存放接收参数的对象;value是待校验的值;callback是回调函数(校验完后,要执行的操作,如抛错)②必须这样,为什么后面有解释

问题重述:如果根据我上面的需求,期望根据传入的test1和test2进行校验,且test1和test2的值是可动态调整,那么该如何实现

// 伪代码
function checkTime(rule, value, callback) {
    if (value === 'test1') {
        callback()
        } else (value === 'test2') {
            callback()
        } else {
            callback()
        }
}

研究发现校验函数要接收的参数直接以此作为一个属性放到校验规则对象中即可。

// 伪代码
rules: {
 startDate: [
	{ validator: checkTime, test1: 'test1', test2: 'test2' }
	],
}

打印rule会有如下结果

// 伪代码
function checkTime(rule, value, callback) {
    console.log(rule, value)
    ...
}

上述说明参数test1和test2已经传入到rule中,剩下的就是根据自己的校验逻辑编写即可。所以element的自定义校验规则如何传参已经解决。

新的思考:我们理解的正常的传参应该如下所示:

正常的传参是

// 伪代码
function  checkTime(test1, test2) {}

// 调用
const validator = checkTime('test1', 'test2')

而上述③

{ validator: checkTime, test1: 'test1', test2: 'test2' }

是如何接收参数的,其中打印的结果中④field,fullField,type等字段并未传递又是怎样出来的?

对于③④我们猜测应该在未知的包中存在如下逻辑代码

// 伪代码
let rule = {}
rule.test1 = rules[startDate][0].test1
rule.test2 = rules[startDate][0].test2
rule.field = rules.prop 即 'startDate'
rule.fullField = rules.prop 即 'startDate'
rule.type = '???'
rule.validator = rules[startDate][0].validator

// 并且存在如下调用
const r = rule.validator(rule, value, callback)

最后通过查看element UI源码发现是由element引用的async-validator包中的调用方式所决定的

在async-validator代码中有下面这样一些处理

// 局部源码
rule.validator = _this.getValidationMethod(rule);
        rule.field = z;
        rule.fullField = rule.fullField || z;
        rule.type = _this.getType(rule);
        
// 局部源码

var res = rule.validator(rule, data.value, cb, data.source, options);

if (res && res.then) {
        res.then(function () {
          return cb();
        }, function (e) {
          return cb(e);
        });
      }

通过上述源码证实了我们的猜想,也验证了②必须以这样的格式(async-validator包中处理格式)实现。

延申:

如果想自己研究源码可以按照如下的方式进行:

找到node_modules包中element-ui包打开packages/form/src可以看到form-item.vue、form.vue、label-wrap.vue三个vue文件,其中label-wrap.vue主要是计算表单项的宽度,form.vue是整体表单逻辑,form-item.vue是表单里具体项逻辑处理,form-item.vue文件中会以此触发一下函数

// step1 300行
mounted() {
    this.addValidateEvents();
}
// step2 288行
methods: {
    addValidateEvents() {
        const rules = this.getRules();

        if (rules.length || this.required !== undefined) {
          this.$on('el.form.blur', this.onFieldBlur);
          // step3 监听表单变化
          this.$on('el.form.change', this.onFieldChange);
        }
      },
    // step4 278行
    onFieldChange() {
        if (this.validateDisabled) {
          this.validateDisabled = false;
          return;
        }

        this.validate('change');
      },
    // step5 189行
    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;
		// step6 207行 此处调用async-validator包
        const validator = new AsyncValidator(descriptor);
        const model = {};

        model[this.prop] = this.fieldValue;
		// step7 到async-validator继续查找
        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);
        });
      }
}

step 8 根据上述导航打开node_modules/async-validator/es/index.js

该文件中定义了一个Schema类,并在该类中定义了validate方法43行

// step9 43hang 
validate: function validate(source_) {}

在该方法中有以下内容

// step10 
validate: function validate(source_) {
    ...
    rule.validator = _this.getValidationMethod(rule); // 124
    rule.field = z; //125
    rule.fullField = rule.fullField || z; //126
    rule.type = _this.getType(rule); 127
    ...
    var res = rule.validator(rule, data.value, cb, data.source, options);// 216
      if (res && res.then) {
        res.then(function () {
          return cb();
        }, function (e) {
          return cb(e);
        });
      }
}

延申二可以在控制台Sources菜单下通过搜索上述所设计到的函数,定位到相应的位置进行debugger,然后自己动手调试调试。

延申三想研究该模块的源码可以参考这位博主的文章Element-UI阅读理解(2) - 表单组件el-form、el-form-item

有关element UI 组件中表单自定义校验规则如何传递参数?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐