element from 表单源码分析, 涉及到 input,select,checkbox,picker,radio 等组件的的验证. 表单的验证用到了 async-validator 插件. 一起来看看吧.
首先 form 表单组件由两部分组成
form: 统一管理 form-item.
form-item: 负责完成验证等.
form
结构
- <form class="el-form" :class="[
- labelPosition ? 'el-form--label-' + labelPosition : '',
- { 'el-form--inline': inline }
- ]">
- <slot></slot>
- </form>
复制代码
结构很简单, form 元素包裹插槽, 也就是 form-item
script 部分
1. 方便与 form-item 关联, 注入 form 实例
- // 注入组件实例
- provide() {
- return {
- elForm: this
- };
- }
复制代码
2. 实例创建后, fields 收集 form-item 的实例
- created() {
- // 监听 el.form.addField 事件, 触发: 将 form-item 实例 push 到 fields
- this.$on('el.form.addField', (field) => {
- if (field) {
- this.fields.push(field);
- }
- });
- // 监听 el.form.removeField 事件, 触发: form-item 实例有 prop 规则属性从 fields 移除 form-item 实例
- this.$on('el.form.removeField', (field) => {
- if (field.prop) {
- this.fields.splice(this.fields.indexOf(field), 1);
- }
- });
- }
复制代码
3. 关于表单验证的一些方法,
form 组件是做是统一管理具体执行还是在 form-item 中
对整个表单进行验证
- // 对整个表单进行验证
- validate(callback) {
- // 没有表单数据 抛警告跳出
- if (!this.model) {
- console.warn('[Element Warn][Form]model is required for validate to work!');
- return;
- }
- let promise;
- // 没有 callback 并且浏览器支持 Promise 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 => {
- // 这里的 validate 是 form-item 的方法
- field.validate('', (message, field) => {
- // 如果有返回信息, 则说明验证失败
- if (message) {
- valid = false;
- }
- // 将错误对象复制到 invalidFields
- invalidFields = objectAssign({}, invalidFields, field);
- // 调 callback
- if (typeof callback === 'function' && ++count === this.fields.length) {
- callback(valid, invalidFields);
- }
- });
- });
- if (promise) {
- return promise;
- }
- }
复制代码
objectAssign 方法在 utils/merge 中, 合并对象的方法
对部分表单进行验证
- // 对部分表单验证
- 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);
- }
复制代码
移除表单项的校验结果
传入待移除的表单项的 prop 属性组成的数组, 如不传则移除整个表单的校验结果
- clearValidate(props = []) {
- const fields = props.length
- ? this.fields.filter(field => props.indexOf(field.prop)> -1)
- : this.fields;
- fields.forEach(field => {
- // form-item 实例的方法 clearValidate(清除验证状态与提示)
- field.clearValidate();
- });
- }
复制代码
对整个表单进行重置, 将所有字段值重置为初始值并移除校验结果
- resetFields() {
- // 没有表单数据 return
- if (!this.model) {
- // 环境变量, 非生产环境抛警告再 return
- process.env.NODE_ENV !== 'production' &&
- console.warn('[Element Warn][Form]model is required for resetFields to work.');
- return;
- }
- this.fields.forEach(field => {
- field.resetField();
- });
- }
复制代码
4. 监听验证规则
- // 监听表单验证规则
- watch: {
- rules() {
- // validateOnRuleChange 未传入 false 则立即触发
- if (this.validateOnRuleChange) {
- // 验证
- this.validate(() => {});
- }
- }
- }
复制代码
form-item
结构
- <div class="el-form-item" :class="[{
- 'el-form-item--feedback': elForm && elForm.statusIcon,
- 'is-error': validateState === 'error',
- 'is-validating': validateState === 'validating',
- 'is-success': validateState === 'success',
- 'is-required': isRequired || required
- },
- sizeClass ? 'el-form-item--' + sizeClass : '' ]">
- <!-- 表单域标签文本 -->
- <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
- <slot name="label">{{label + form.labelSuffix}}</slot>
- </label>
- <div class="el-form-item__content" :style="contentStyle">
- <!-- 插槽接收表单验证的元素, input 框单选框多选框之类的 -->
- <slot></slot>
- <!-- 验证不通过时的 message -->
- <transition name="el-zoom-in-top">
- <div
- v-if="validateState ==='error'&& showMessage && form.showMessage"
- class="el-form-item__error"
- :class="{
- 'el-form-item__error--inline': typeof inlineMessage === 'boolean'
- ? inlineMessage
- : (elForm && elForm.inlineMessage || false)
- }"
- >
- {{validateMessage}}
- </div>
- </transition>
- </div>
- </div>
复制代码
外层 div 控制整体样式, 内部分为两部分
第一部分: 可展示 label 或拼接统一的后缀 (from 的属性)
第二部分: slot 接收 input,select 等组件, 以及验证不通过时的 message 展示
这里的变量 elForm 就是 form 组件 provide 的 from 实例
script 部分
1.inject 接收 form 实例, 并向子孙后代注入 form-item 实例
- provide() { // 注入 form-item 实例
- return {
- elFormItem: this
- };
- },
- // 接收 form 实例
- inject: ['elForm']
复制代码
2. 组件 $el 挂载到实例后
初始化, 需要验证的让 form 组件收集起来, 有验证规则的 el.form.blur ,el.form.change 事件监听起来, 等待触发验证.
- mounted() {
- // 有需要验证的表单
- if (this.prop) {
- // 向上查找 form 组件, 并发布 el.form.addField, 暴露 form-item 实例
- // 即让 form 组件收集需要验证的 form-item 实例
- this.dispatch('ElForm', 'el.form.addField', [this]);
- // 需要验证的表单数据
- let initialValue = this.fieldValue;
- // 是数组
- if (Array.isArray(initialValue)) {
- initialValue = [].concat(initialValue);
- }
- // 响应属性变成普通属性
- Object.defineProperty(this, 'initialValue', {
- value: initialValue
- });
- // 该项验证规则
- let rules = this.getRules();
- if (rules.length || this.required !== undefined) {
- // 监听 el.form.blur, 回调为 bluer 事件验证
- // 监听 el.form.change 事件, 回调为 change 事件验证
- this.$on('el.form.blur', this.onFieldBlur);
- this.$on('el.form.change', this.onFieldChange);
- }
- }
- }
复制代码
这里用到的 dispatch 方法从 mixins 中引入: 找到指定组件, 发布指定事件.
3. 验证方法
- validate(trigger, callback = noop) {
- this.validateDisabled = false;
- // 符合规则的 trigger
- const rules = this.getFilteredRule(trigger);
- // 没有规则也不是必填 返回 true
- if ((!rules || rules.length === 0) && this.required === undefined) {
- // 执行回调
- callback();
- return true;
- }
- // 验证中
- this.validateState = 'validating';
- const descriptor = {};
- // 为了匹配 AsyncValidator 插件所需要的格式, 需要做规则数据做一些操作
- if (rules && rules.length> 0) {
- rules.forEach(rule => {
- delete rule.trigger;
- });
- }
- // AsyncValidator 需要的验证规则
- descriptor[this.prop] = rules;
- // 验证规则 AsyncValidator 实例对象
- const validator = new AsyncValidator(descriptor);
- const model = {};
- // AsyncValidator 需要的验证数据
- 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);
- // form 组件发布 validate 事件
- this.elForm && this.elForm.$emit('validate', this.prop, !errors);
- });
- }
复制代码
4. 清空验证状态与重置表单验证方法
- // 清空验证状态及 message
- clearValidate() {
- // 验证状态
- this.validateState = '';
- // 验证 message
- this.validateMessage = '';
- this.validateDisabled = false;
- },
- // 重置
- resetField() {
- this.validateState = '';
- this.validateMessage = '';
- // 拿到初始数据
- let model = this.form.model;// 所以表单数据
- let value = this.fieldValue; // 该项表单数据
- let path = this.prop; // 该项
- if (path.indexOf(':') !== -1) {
- path = path.replace(/:/, '.');
- }
- // 该项表单数据
- let prop = getPropByPath(model, path, true);
- this.validateDisabled = true;
- // 重置为初始表单数据
- if (Array.isArray(value)) {
- prop.o[prop.k] = [].concat(this.initialValue);
- } else {
- prop.o[prop.k] = this.initialValue;
- }
- // 向下寻找 select 组件, 发布 fieldReset 事件暴露初始表单数据
- this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue);
- }
复制代码
5. 监听 error 以及验证状态
这里的 error 是 props 接收的: 若传入, 状态变为 error, 并显示错误信息
- watch: {
- // 监听 error
- error: {
- // 立即执行 handler
- immediate: true,
- handler(value) {
- // 验证状态变为 error, 并显示错误信息
- this.validateMessage = value;
- this.validateState = value ? 'error' : '';
- }
- },
- // 监听验证状态
- validateStatus(value) {
- this.validateState = value;
- }
- }
复制代码
6. 实例销毁之前, 发布 form 移除收集的该 form-item 实例
- beforeDestroy() {
- // 向上寻找 form 组件, 发布 el.form.removeField 事件, 暴露当前实例
- this.dispatch('ElForm', 'el.form.removeField', [this]);
- }
复制代码
form 表单组件与 input 等组件的关联
是怎么触发 from 验证的呢
form 在 mounted 时监听了 el.form.blur 和 el.form.change 事件, 并指定了验证的回调函数
- this.$on('el.form.blur', this.onFieldBlur);
- this.$on('el.form.change', this.onFieldChange);
复制代码
以 input 为例
- // form 表单发布 el.form.blur 事件
- this.dispatch('ElFormItem', 'el.form.blur', [this.currentValue]);
复制代码
以 checkbox 多选为例
- // 监听 value 向上寻找 form 组件发布 el.form.change 事件暴露 value(数组)
- this.dispatch('ElFormItem', 'el.form.change', [value]);
复制代码
等... , 这些时机触发校验
$on 就是监听指定的事件并且指定回调函数,$emit 就是发布某个事件并传递某些数据 (可不传), 当监听的事件名与发布的事件名一致就会触发监听的回调函数并且参数就是对应 $emit 传递的参数.
来源: https://juejin.im/post/5b99ff0af265da0a8a6a9439