返回博客

el-select 选了值还报「必填」——Element UI 表单验证触发器踩坑

在某个后台管理系统里,一个内容发布表单让我卡了将近半小时。表单里有文本输入、下拉选择、日期选择、单选组——全都加了 required: true 的规则。文本框一切正常:失焦就验证,点提交也验证。唯独那个「选择模板」的下拉框,明明已经选了一个值,点提交还是弹出「请选择千人千面模板」的错误提示。

打开 Vue Devtools 确认了:this.form.templateId 里有值,不是 undefined 也不是空字符串。但 el-form 就是认为这个字段没通过验证。

现象

  1. 进入表单页,el-select 显示 placeholder。
  2. 展开下拉,选中一项,下拉收起,v-model 绑定值正确更新。
  3. 点「提交」——表单报错,el-select 下方出现红色错误文案。
  4. 再次点「提交」——依旧报错,直到你用鼠标点一下其他输入框让其失焦,错误才消失。

诡异之处:如果把这个字段从 el-select 改成 el-input,同样的规则立刻能正常工作。

根因

el-form 的底层验证引擎是 async-validator。每条规则都绑定一个触发事件(trigger)。当没有显式声明 trigger 时,默认值是 "blur"

el-input 失焦时会 emit blur,所以 blur 触发器对文本框来说天然正确。

el-select 的核心用户交互是展开列表 → 点击选项 → 列表收起,整个过程 emit 的是 change不是 blurblur 只有在下拉收起后用户再点别处时才可能触发,且跨浏览器行为不一致,根本不可靠。

同样不 emit blur 的还有:

  • el-date-picker / el-time-picker
  • el-checkbox / el-checkbox-group
  • el-radio / el-radio-group
  • el-cascader
  • el-switch
  • el-rate
  • 任何内部用 popover / dropdown 实现的自定义组件

所以规则存在,验证函数本身也正确,但触发器永远等不到那个 blur 事件——验证从来没跑过。

坑:el-select 不 emit blur,默认触发器永远不触发

el-form-item 的规则默认 trigger: "blur",而 el-selectel-date-pickerel-radio-group 等选择类组件选值时 emit 的是 change,不是 blur

这意味着:你写了规则、用户也选了值,但验证逻辑从来没执行过

正解

针对选择类组件,每条规则显式加 trigger: "change"。混合表单(既有文本框又有下拉)要按组件类型分别声明触发器:

rules: {
  // el-input —— blur 正确,保持默认
  title: [
    { required: true, message: "请输入标题", trigger: "blur" },
    { max: 25, message: "长度不超过 25 字", trigger: "blur" },
  ],
 
  // el-select —— 必须改成 change
  templateId: [
    { required: true, message: "请选择模板", trigger: "change" },
  ],
 
  // el-date-picker —— 同样是 change
  publishTime: [
    { required: true, message: "请选择发布时间", trigger: "change" },
  ],
 
  // el-radio-group —— change
  userType: [
    { required: true, message: "请选择用户类型", trigger: "change" },
  ],
}

如果表单以选择类组件为主(过滤面板、设置页),可以封装一个小工具函数减少重复:

const rc = (msg) => ({ required: true, message: msg, trigger: "change" })
 
rules: {
  city:    [rc("请选择城市")],
  status:  [rc("请选择状态")],
  channel: [rc("请选择渠道")],
}
正解:选择类组件规则一律加 trigger: 'change'

trigger 要和组件实际 emit 的事件匹配:

  • el-input / el-textarea"blur"(默认即可)
  • el-select / el-date-picker / el-radio-group / el-checkbox-group / el-cascader / el-switch"change"

一个规则也可以同时监听两个触发器:trigger: ["blur", "change"],适合自定义组件两者都 emit 的情况。

Code Review 快速扫描

维护老项目时,一条 grep 就能找出漏网的规则:

# 找出有 required:true 但没声明 trigger 的规则行
grep -rn 'required:\s*true' src/ --include="*.vue" | grep -v 'trigger'

对每个命中项检查模板里对应字段用的是什么组件,如果是 el-select / el-date-picker 类,补上 trigger: "change" 即可。

延伸:Element Plus 和其他 UI 库

Element Plus(Vue 3 版本)有相同的默认行为,修法完全一样。iview / ant-design-vue 也遵循类似的「文本框 blur、选择框 change」惯例,同样需要显式声明触发器。

一句话外卖

el-form 写规则时,先问自己「这个组件 emit 的是 blur 还是 change」——选择类组件的触发器必须改成 "change",否则验证逻辑永远是死代码。