el-select 选了值还报「必填」——Element UI 表单验证触发器踩坑
在某个后台管理系统里,一个内容发布表单让我卡了将近半小时。表单里有文本输入、下拉选择、日期选择、单选组——全都加了 required: true 的规则。文本框一切正常:失焦就验证,点提交也验证。唯独那个「选择模板」的下拉框,明明已经选了一个值,点提交还是弹出「请选择千人千面模板」的错误提示。
打开 Vue Devtools 确认了:this.form.templateId 里有值,不是 undefined 也不是空字符串。但 el-form 就是认为这个字段没通过验证。
现象
- 进入表单页,
el-select显示 placeholder。 - 展开下拉,选中一项,下拉收起,v-model 绑定值正确更新。
- 点「提交」——表单报错,
el-select下方出现红色错误文案。 - 再次点「提交」——依旧报错,直到你用鼠标点一下其他输入框让其失焦,错误才消失。
诡异之处:如果把这个字段从 el-select 改成 el-input,同样的规则立刻能正常工作。
根因
el-form 的底层验证引擎是 async-validator。每条规则都绑定一个触发事件(trigger)。当没有显式声明 trigger 时,默认值是 "blur"。
el-input 失焦时会 emit blur,所以 blur 触发器对文本框来说天然正确。
但 el-select 的核心用户交互是展开列表 → 点击选项 → 列表收起,整个过程 emit 的是 change,不是 blur。blur 只有在下拉收起后用户再点别处时才可能触发,且跨浏览器行为不一致,根本不可靠。
同样不 emit blur 的还有:
el-date-picker/el-time-pickerel-checkbox/el-checkbox-groupel-radio/el-radio-groupel-cascaderel-switchel-rate- 任何内部用 popover / dropdown 实现的自定义组件
所以规则存在,验证函数本身也正确,但触发器永远等不到那个 blur 事件——验证从来没跑过。
el-form-item 的规则默认 trigger: "blur",而 el-select、el-date-picker、el-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 要和组件实际 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(Vue 3 版本)有相同的默认行为,修法完全一样。iview / ant-design-vue 也遵循类似的「文本框 blur、选择框 change」惯例,同样需要显式声明触发器。
一句话外卖
给
el-form写规则时,先问自己「这个组件 emit 的是 blur 还是 change」——选择类组件的触发器必须改成"change",否则验证逻辑永远是死代码。