element-ui表單原始碼解析之el-form

談笑斗酒發表於2019-03-16

表單時大家常用的,根據本站的百度統計後臺顯示來到道招網的程式很多都在關注《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時,才表示校驗成功。這樣是沒錯,但是如果你的專案是分多個表單,並且需要幾個表單都校驗通過才能提交,你是不是覺得不那麼好寫了。 其實官方已經給出了說明了

element-ui表單原始碼解析之el-form
只是說就差給個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的原始碼應該是講的很清楚了,有需要的同學可以檢視原始碼。

相關文章