• element form源码


    form.vue

    <template>
      <form class="el-form" :class="[
        labelPosition ? 'el-form--label-' + labelPosition : '',
        { 'el-form--inline': inline }
      ]">
        <slot></slot>
      </form>
    </template>
    <script>
      import objectAssign from 'element-ui/src/utils/merge';
    
      export default {
        name: 'ElForm',
    
        componentName: 'ElForm',
        // 注入自身,子孙组件接收不管层级多深
        provide() {
          return {
            elForm: this
          };
        },
    
        props: {
          // model    表单数据对象
          model: Object,
          // rules    表单验证规则
          rules: Object,
          // 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 label-width
          labelPosition: String,
          // 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto。
          labelWidth: String,
          // 表单域标签的后缀
          labelSuffix: {
            type: String,
            default: ''
          },
          inline: Boolean,
          // 是否以行内形式展示校验信息
          inlineMessage: Boolean,
          // 是否在输入框中显示校验结果反馈图标
          statusIcon: Boolean,
          // 是否显示校验错误信息
          showMessage: {
            type: Boolean,
            default: true
          },
          // 用于控制该表单内组件的尺寸
          size: String,
          // 是否禁用该表单内的所有组件。若设置为 true,则表单内组件上的 disabled 属性不再生效
          disabled: Boolean,
          // 是否在 rules 属性改变后立即触发一次验证
          validateOnRuleChange: {
            type: Boolean,
            default: true
          },
          // 是否显示必填字段的标签旁边的红色星号
          hideRequiredAsterisk: {
            type: Boolean,
            default: false
          }
        },
        watch: {
          rules() {
            // remove then add event listeners on form-item after form rules change
            this.fields.forEach(field => {
              field.removeValidateEvents();
              field.addValidateEvents();
            });
    
            if (this.validateOnRuleChange) {
              this.validate(() => {});
            }
          }
        },
        computed: {
          autoLabelWidth() {
            if (!this.potentialLabelWidthArr.length) return 0;
            const max = Math.max(...this.potentialLabelWidthArr);
            return max ? `${max}px` : '';
          }
        },
        data() {
          return {
            fields: [],
            potentialLabelWidthArr: [] // use this array to calculate auto width
          };
        },
        created() {
          // 接收子组件添加rule验证事件
          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);
            }
          });
        },
        methods: {
          // 重置验证
          resetFields() {
            if (!this.model) {
              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();
            });
          },
          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;
            }
          },
          validateField(props, cb) {
            props = [].concat(props);
            const fields = this.fields.filter(field => props.indexOf(field.prop) !== -1);
            if (!fields.length) {
              console.warn('[Element Warn]please pass correct props!');
              return;
            }
    
            fields.forEach(field => {
              field.validate('', cb);
            });
          },
          // 获取label的下标
          getLabelWidthIndex(width) {
            const index = this.potentialLabelWidthArr.indexOf(width);
            // it's impossible
            if (index === -1) {
              throw new Error('[ElementForm]unpected width ', width);
            }
            return index;
          },
          // 重置labelwidth
          registerLabelWidth(val, oldVal) {
            if (val && oldVal) {
              const index = this.getLabelWidthIndex(oldVal);
              this.potentialLabelWidthArr.splice(index, 1, val);
            } else if (val) {
              this.potentialLabelWidthArr.push(val);
            }
          },
          // 移除此宽度
          deregisterLabelWidth(val) {
            const index = this.getLabelWidthIndex(val);
            this.potentialLabelWidthArr.splice(index, 1);
          }
        }
      };
    </script>

    label-warp.vue

    <script>
    
    export default {
      props: {
        isAutoWidth: Boolean,
        updateAll: Boolean
      },
      // 接收el-form和el-form-item
      inject: ['elForm', 'elFormItem'],
    
      render() {
        const slots = this.$slots.default;
        if (!slots) return null;
        if (this.isAutoWidth) {
          const autoLabelWidth = this.elForm.autoLabelWidth;
          const style = {};
          if (autoLabelWidth && autoLabelWidth !== 'auto') {
            const marginLeft = parseInt(autoLabelWidth, 10) - this.computedWidth;
            if (marginLeft) {
              style.marginLeft = marginLeft + 'px';
            }
          }
          return (<div class="el-form-item__label-wrap" style={style}>
            { slots }
          </div>);
        } else {
          return slots[0];
        }
      },
    
      methods: {
        // 获取第一个元素的宽度
        getLabelWidth() {
          if (this.$el && this.$el.firstElementChild) {
            const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
            return Math.ceil(parseFloat(computedWidth));
          } else {
            return 0;
          }
        },
        updateLabelWidth(action = 'update') {
          if (this.$slots.default && this.isAutoWidth && this.$el.firstElementChild) {
            if (action === 'update') {
              // 第一个元素宽度座位此组件的宽度
              this.computedWidth = this.getLabelWidth();
            } else if (action === 'remove') {
              // 调用父组件方法
              this.elForm.deregisterLabelWidth(this.computedWidth);
            }
          }
        }
      },
    
      watch: {
        // 检测computedWidth变化
        computedWidth(val, oldVal) {
          if (this.updateAll) {
            // 更新父组件label
            this.elForm.registerLabelWidth(val, oldVal);
            // 更新当前item的label
            this.elFormItem.updateComputedLabelWidth(val);
          }
        }
      },
    
      data() {
        return {
          computedWidth: 0
        };
      },
    
      mounted() {
        this.updateLabelWidth('update');
      },
    
      updated() {
        this.updateLabelWidth('update');
      },
    
      beforeDestroy() {
        this.updateLabelWidth('remove');
      }
    };
    </script>

    form-item.vue

    <template>
      <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,
          'is-no-asterisk': elForm && elForm.hideRequiredAsterisk
        },
        sizeClass ? 'el-form-item--' + sizeClass : ''
      ]">
        <label-wrap
          :is-auto-width="labelStyle && labelStyle.width === 'auto'"
          :update-all="form.labelWidth === 'auto'">
          <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
            <slot name="label">{{label + form.labelSuffix}}</slot>
          </label>
        </label-wrap>
        <div class="el-form-item__content" :style="contentStyle">
          <slot></slot>
          <transition name="el-zoom-in-top">
            <slot
              v-if="validateState === 'error' && showMessage && form.showMessage"
              name="error"
              :error="validateMessage">
              <div
                class="el-form-item__error"
                :class="{
                  'el-form-item__error--inline': typeof inlineMessage === 'boolean'
                    ? inlineMessage
                    : (elForm && elForm.inlineMessage || false)
                }"
              >
                {{validateMessage}}
              </div>
            </slot>
          </transition>
        </div>
      </div>
    </template>
    <script>
      import AsyncValidator from 'async-validator';
      import emitter from 'element-ui/src/mixins/emitter';
      import objectAssign from 'element-ui/src/utils/merge';
      import { noop, getPropByPath } from 'element-ui/src/utils/util';
      import LabelWrap from './label-wrap';
      export default {
        name: 'ElFormItem',
    
        componentName: 'ElFormItem',
    
        mixins: [emitter],
    
        provide() {
          return {
            elFormItem: this
          };
        },
    
        inject: ['elForm'],
    
        props: {
          label: String,
          labelWidth: String,
          prop: String,
          required: {
            type: Boolean,
            default: undefined
          },
          rules: [Object, Array],
          error: String,
          validateStatus: String,
          for: String,
          inlineMessage: {
            type: [String, Boolean],
            default: ''
          },
          showMessage: {
            type: Boolean,
            default: true
          },
          size: String
        },
        components: {
          // use this component to calculate auto width
          LabelWrap
        },
        watch: {
          error: {
            immediate: true,
            handler(value) {
              this.validateMessage = value;
              this.validateState = value ? 'error' : '';
            }
          },
          // 验证状态
          validateStatus(value) {
            this.validateState = value;
          }
        },
        computed: {
          labelFor() {
            return this.for || this.prop;
          },
          // labelStyle的样式
          labelStyle() {
            const ret = {};
            if (this.form.labelPosition === 'top') return ret;
            const labelWidth = this.labelWidth || this.form.labelWidth;
            if (labelWidth) {
              ret.width = labelWidth;
            }
            return ret;
          },
          // 内容的样式
          contentStyle() {
            const ret = {};
            const label = this.label;
            if (this.form.labelPosition === 'top' || this.form.inline) return ret;
            if (!label && !this.labelWidth && this.isNested) return ret;
            const labelWidth = this.labelWidth || this.form.labelWidth;
            if (labelWidth === 'auto') {
              if (this.labelWidth === 'auto') {
                ret.marginLeft = this.computedLabelWidth;
              } else if (this.form.labelWidth === 'auto') {
                ret.marginLeft = this.elForm.autoLabelWidth;
              }
            } else {
              ret.marginLeft = labelWidth;
            }
            return ret;
          },
          form() {
            let parent = this.$parent;
            let parentName = parent.$options.componentName;
            while (parentName !== 'ElForm') {
              if (parentName === 'ElFormItem') {
                this.isNested = true;
              }
              parent = parent.$parent;
              parentName = parent.$options.componentName;
            }
            return parent;
          },
          // 获取当前项的值
          fieldValue() {
            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;
          },
          // 是否必填
          isRequired() {
            let rules = this.getRules();
            let isRequired = false;
    
            if (rules && rules.length) {
              rules.every(rule => {
                if (rule.required) {
                  isRequired = true;
                  return false;
                }
                return true;
              });
            }
            return isRequired;
          },
          _formSize() {
            return this.elForm.size;
          },
          elFormItemSize() {
            return this.size || this._formSize;
          },
          sizeClass() {
            return this.elFormItemSize || (this.$ELEMENT || {}).size;
          }
        },
        data() {
          return {
            validateState: '',
            validateMessage: '',
            validateDisabled: false,
            validator: {},
            isNested: false,
            computedLabelWidth: ''
          };
        },
        methods: {
          // 验证
          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);
            });
          },
          //  清除验证
          clearValidate() {
            this.validateState = '';
            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;
            }
            // 通知ElTimeSelect,执行fieldReset事件,传值initialValue
            this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue);
          },
          // 获取rules
          getRules() {
            let formRules = this.form.rules;
            // 当前组件rules,默认为空[]
            const selfRules = this.rules;
            const requiredRule = this.required !== undefined ? { required: !!this.required } : [];
            // 获取当前项对象
            const prop = getPropByPath(formRules, this.prop || '');
            formRules = formRules ? (prop.o[this.prop || ''] || prop.v) : [];
    
            return [].concat(selfRules || formRules || []).concat(requiredRule);
          },
          // 
          getFilteredRule(trigger) {
            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));
          },
          // 触发失焦
          onFieldBlur() {
            this.validate('blur');
          },
          onFieldChange() {
            if (this.validateDisabled) {
              this.validateDisabled = false;
              return;
            }
            // 触发change事件
            this.validate('change');
          },
          updateComputedLabelWidth(width) {
            this.computedLabelWidth = width ? `${width}px` : '';
          },
          // 添加验证事件
          addValidateEvents() {
            const rules = this.getRules();
    
            if (rules.length || this.required !== undefined) {
              // 监听form表单blur事件,触发onFieldBlur
              this.$on('el.form.blur', this.onFieldBlur);
              // 监听form表单chang事件
              this.$on('el.form.change', this.onFieldChange);
            }
          },
          // 移除验证事件
          removeValidateEvents() {
            this.$off();
          }
        },
        mounted() {
          if (this.prop) {
            // 通知ElForm组件el.form.addField方法,传递this
            this.dispatch('ElForm', 'el.form.addField', [this]);
            // 初始化的值
            let initialValue = this.fieldValue;
            if (Array.isArray(initialValue)) {
              initialValue = [].concat(initialValue);
            }
            /**
              语法:
              Object.defineProperty(obj, prop, descriptor)
              参数说明:
    
              obj:必需。目标对象 
              prop:必需。需定义或修改的属性的名字
              descriptor:必需。目标属性所拥有的特性
              返回值:
    
              传入函数的对象。即第一个参数obj
             */
            // 设置初始值
            Object.defineProperty(this, 'initialValue', {
              value: initialValue
            });
            // 设置监听事件
            this.addValidateEvents();
          }
        },
        // 销毁
        beforeDestroy() {
          this.dispatch('ElForm', 'el.form.removeField', [this]);
        }
      };
    </script>
  • 相关阅读:
    【解决】Git failed with a fatal error. Authentication failed for ‘http://......’
    利用BenchmarkDotNet 测试 .Net Core API 同步和异步方法性能
    mysql通过event和存储过程实时更新简单Demo
    执行命令npm install XXX后仍然提示 Cannot find Module XXX
    c#调用微信接口获取token值
    同一对象多条数据同时插入数据库
    常用的正则表达式(持续更新。。)
    【半转贴】解决SQL SERVER 2008数据库表中修改字段后不能保存
    asp.net中的服务器控件button的属性
    关于在asp.net中textbox文本输入框中的汉语标点符号显示位置的问题
  • 原文地址:https://www.cnblogs.com/wsk1576025821/p/10951396.html
Copyright © 2020-2023  润新知