非el组件/自定义组件触发el-form的校验

参考文档:https://zhuanlan.zhihu.com/p/390466860

问题描述

非el组件像原生的input、自定义组件等,无法触发el-form的rules校验,如下面的代码:

<el-form ref="passwordForm"
         :model="passwordForm"
         :rules="rules">
  <el-form-item label="密码"
                prop="oldPassword">
    <el-input type="password" auto-complete="off"
              v-model="passwordForm.oldPassword" />
  </el-form-item>
  <el-form-item label="新密码"
                prop="newPassword">
    <password-input v-model="passwordForm.newPassword" />
  </el-form-item>
  <el-form-item label="确认新密码"
                prop="newPasswordCheck">
    <password-input v-model="passwordForm.passwordCheck" />
  </el-form-item>
</el-form>
export default {
  data() {
    var validatePasswordCheck = (rule, value, callback) => {
      if (value !== this.passwordForm.newPassword) {
        callback(new Error('两次输入密码不一致!'))
      } else {
        callback()
      }
    }
    return {
      passwordForm: {
        oldPassword: '',
        newPassword: '',
        newPasswordCheck: ''
      },
      rules: {
        oldPassword: [
          { required: true, message: '请输入密码', trigger: 'blur' }
        ],
        newPassword: [
          { required: true, message: '请输入新密码', trigger: 'blur' }
        ],
        newPasswordCheck: [
          { required: true, message: '请再次输入新密码', trigger: 'blur' },
          { validator: this.validatePasswordCheck, trigger: 'blur' }
        ]
      }
    }
  }
}

第一个el-form-itemel-input组件可以触发校验,但是第二个和第三个el-form-itempassword-input是自定义组件,无法触发校验。

源码分析

源码地址: https://github.com/ElemeFE/element
在packages/form/src/form-item.vue中,可以找到addValidateEvents方法,该方法是用来给el-form-item的子组件绑定校验事件的,如下:

addValidateEvents() {
  const rules = this.getRules();
  if (rules.length || this.required !== undefined) {
    this.$on('el.form.blur', this.onFieldBlur);
    this.$on('el.form.change', this.onFieldChange);
  }
}

packages/input/src/input.vue中,可以找到el-input发送el.form.blurel.form.change事件的代码,这里只贴出el.form.change的代码:

watch: {
  value(val) {
    this.$nextTick(this.resizeTextarea);
    if (this.validateEvent) {
      this.dispatch('ElFormItem', 'el.form.change', [val]);
    }
  }
}

这里用了dispatch方法,该方法的代码在src/mixins/emitter.js中:

dispatch(componentName, eventName, params) {
  var parent = this.$parent || this.$root;
  var name = parent.$options.componentName;
  while (parent && (!name || name !== componentName)) {
    parent = parent.$parent;
    if (parent) {
      name = parent.$options.componentName;
    }
  }
  if (parent) {
    parent.$emit.apply(parent, [eventName].concat(params));
  }
}

由此可以看出,要触发el-form的校验,需要el-form-item中的子组件去发布el.form.changeel.form.blur等事件,由el-form-item监听该事件,触发表单校验。

解决方案

  1. 方法一:在父页面中直接调用表单的校验方法validateField:
watch: {
  'passwordForm.newPassword': function() {
    this.$refs.passwordForm.validateField('newPassword')
  }
}
  1. 方法二:在父页面中发布组件的el.form.change等事件:
<input ref="input" @blur="handleBlur">
<script>
export default {
  methods: {
    handleBlur (val) {
      this.$refs.input.$emit('el.form.blur', val)
    }
  }

}
</script>
  1. 方法三:在子组件中发布el.form.change等事件,此时无需在父页面中做任何处理,其中dispatch方法直接将上面所说的emitter.js中的代码拷贝过来即可:
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      // ... 从emitter.js中拷贝过来的代码
    },
    handleInput (e) {
      this.$emit('input', e.target.value)
      this.dispatch('ElFormItem', 'el.form.change', [e.target.value])
    }
  }
}

得到的效果如下图所示:

不过输入框的颜色没变成红色,查看el-input的样式可以看到其错误时的颜色是通过.el-form-item.is-error .el-input__inner选择器来控制的:

.el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus, .el-message-box__input input.invalid, .el-message-box__input input.invalid:focus {
    border-color: #F56C6C;
}

类似的,可以给自定义组件也加上el-input__inner类名,便能实现错误时的样式了。
效果如下图所示:

posted @ 2023-04-15 05:10  longxiaoming  阅读(808)  评论(0编辑  收藏  举报