阅读 3921

element-ui表单源码解析之el-form

表单时大家常用的,根据本站的百度统计后台显示来到道招网的程序很多都在关注《element-ui动态表单async-validate校验 please transfer a valid prop path to form item!》 看来很多网友对element-ui的校验(或者说是async-validator)使用不太熟悉,想了想还是有必要分享下element-ui的表单校验机制的。

首先表单校验分el-form, el-form-item以及里面的el-input, el-select等。 这里就以el-form, el-form-item和el-input组合为例。为了讲的更加详细,决定分一个系列来讲。毕竟码字多了就想草草结束。。。 先直接看el-form吧,这里的代码很少,毕竟主要内容是以slot插入的。

<template>
  <form class="el-form" :class="[
    labelPosition ? 'el-form--label-' + labelPosition : '',
    { 'el-form--inline': inline }
  ]">
    <slot></slot>
  </form>
</template>
复制代码

属性就不多说了,跟校验相关的就是model和rules。还有就是validateOnRuleChange,它默认是true,并且有这样的watch存在

watch: {
	rules() {
		if (this.validateOnRuleChange) {
			this.validate(() => {});
		}
	}
},
复制代码

默认规则改变会实时校验表单哦。

// 表单校验源码
validate(callback) {
	if (!this.model) {
		console.warn('[Element Warn][Form]model is required for validate to work!');
		return;
	}

	let promise;
	// if no callback, return promise
	if (typeof callback !== 'function' && window.Promise) {
		promise = new window.Promise((resolve, reject) => {
			callback = function(valid) {
				valid ? resolve(valid) : reject(valid);
			};
		});
	}

	let valid = true;
	let count = 0;
	// 如果需要验证的fields为空,调用验证时立刻返回callback
	if (this.fields.length === 0 && callback) {
		callback(true);
	}
	let invalidFields = {};
	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);
			}
		});
	});

	if (promise) {
		return promise;
	}
},
复制代码

官网的示例是这样的

methods: {
      submitForm(formName) {
        this.$refs[formName].validate((valid) => {
          if (valid) {
            alert('submit!');
          } else {
            console.log('error submit!!');
            return false;
          }
        });
      },
      resetForm(formName) {
        this.$refs[formName].resetFields();
      }
    }
复制代码

所以很多初学者是不是一直在用callback的方式啊,里面的参数为true时,才表示校验成功。这样是没错,但是如果你的项目是分多个表单,并且需要几个表单都校验通过才能提交,你是不是觉得不那么好写了。 其实官方已经给出了说明了

只是说就差给个demo了,毕竟部分初学者喜欢复制然后修改嘛。 我们根据上面的源码可以看出想使用校验,必须传递model属性。如果传递的callback不是方法的话,会定义一个promise,同时会定义callback为方法,参数依然是是否校验通过的结果。个人觉得这样还是很巧妙的,毕竟**后面就可以完全按照传递了callback为前提继续写了,中途不用来回想如果用户是传递callback的模式呢还是直接使用promise的模式了,只是说最后发现如果有promise就返回即可。**点个赞。 接着看this.fields,这是个数组,存储了哪些el-form-item是需要校验的。在created时进行存储的。 因为vue的父组件的created周期是在子组件的created之前的。

// el-form源码
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);
		}
	});
},
复制代码

里面同时监听了移除校验某个el-form-item的事件。 到此el-form源码里面剩下的就不多了。只剩下以下三个方法了,放一起吧。

// el-form源码
		resetFields() {
			if (!this.model) {
				process.env.NODE_ENV !== 'production' &&
					console.warn('[Element Warn][Form]model is required for resetFields to work.');
				return;
			}
			this.fields.forEach(field => {
				field.resetField();
			});
		},
		clearValidate(props = []) {
			const fields = props.length
			? (typeof props === 'string'
			   ? this.fields.filter(field => props === field.prop)
			   : this.fields.filter(field => props.indexOf(field.prop) > -1)
			  ) : this.fields;
			fields.forEach(field => {
				field.clearValidate();
			});
		},
		validateField(prop, cb) {
			let field = this.fields.filter(field => field.prop === prop)[0];
			if (!field) { throw new Error('must call validateField with valid prop string!'); }

			field.validate('', cb);
		}
复制代码

分别是重置表单、清空校验、单独校验某个el-form-item

  1. 重置表单:就是让里面每个item各自重置自己即可。
  2. 清空校验:支持清空针对某个prop的校验,可以是字符串,也可以是数组。如果什么都不传的话,就去情况整个表单的校验了。
  3. 单独校验某个item,根据传入的prop过滤出对应的field,如果有多个只取第一个,然后校验,执行回调。

需要说明下的是: 这里的field.validate需要在el-form-item里面看了。 el-form通过下面的方式实现了在它的子组件里面能够通过this.elForm来访问el-form的功能。Vue2.2.0+的功能

// el-form源码
provide() {
	return {
		elForm: this
	};
},
复制代码

el-form的源码应该是讲的很清楚了,有需要的同学可以查看源码。

文章分类
前端
文章标签