ElSelect 多选远程搜索选项丢失问题
1. 问题现象描述
我们在使用 Element Plus 中的 ElSelect 组件时,常会使用 remote + multiple 的组合来实现“远程搜索 + 多选下拉框”。然而:
选中第一个选项正常
搜索新关键词,选中第二个选项后,第一个选项会莫名其妙被清除(即选项列表和选中值都被重置)。
2. 复现代码
<ElSelectv-model="selectedIds"remotefilterablemultipleremote-method="getCompanyList"placeholder="请选择公司"
><ElOptionv-for="item in companyList":key="item.id":label="item.name":value="item.id"/>
</ElSelect>
const selectedIds = ref([]);
const companyList = ref([]);const getCompanyList = async (query) => {const res = await fetchCompanyList(query);companyList.value = res.items; // ❌ 直接覆盖
};
3. 问题产生的原因(底层机制)
Element Plus 中 remote 模式的机制,来自官方文档说明(见 ElSelect 文档 - 远程搜索)。
在 remote 模式下,组件只会根据传入的 ElOption 列表,来匹配 v-model 中的值用于展示。
若当前选中项不在 options(即下拉数据)中,组件无法渲染标签文字,甚至会清除该项。
⚠ 问题根本原因:
每次搜索时,覆盖了 companyList,这个列表是 ElOption 渲染数据源。
companyList.value = res.items // 会丢掉之前选中的项
于是:
- 当前已选的第一个公司 ID 不再存在于新的 companyList 中;
- ElSelect 组件找不到对应的 label,只能移除该 ID;
- 结果:选中第二项后,第一个值直接“消失”了。
本质上,这是一个“视图与状态解耦”的问题:
- v-model 存的是“选中值”;
- 但 UI 渲染所依赖的 ElOption 是当前 companyList;
- 如果 ElOption 没有包含 v-model 中的项,则 UI 会清除它。
4. 解决方法:合并搜索结果 + 已选项 + 去重
修改 getCompanyList
const getCompanyList = async (val) => {const res = await fetchCompanyList(val);const newList = res.items.map(item => ({ ...item, id: String(item.id) }));const selectedIds = selectedIds.value.map(String);const selectedOptions = companyList.value.filter(item => selectedIds.includes(String(item.id)));const merged = Array.from(new Map([...selectedOptions, ...newList].map(item => [item.id, item])).values());companyList.value = merged;
};
关键点:
要点 | 描述 |
---|---|
保留原选中项 | 防止它们在新 options 中丢失 |
合并数据 | 旧选项 + 新搜索结果 |
去重 | 使用 Map 以 id 为键去重 |
强制类型一致 | id 全部转成 String,避免 '123' !== 123 |
5. 其他解决方法
1、使用 value-key 并绑定对象数组
<ElSelectv-model="selectedCompanyObjects"value-key="id"
><ElOption :value="company" v-for="company in companyList" :label="company.name" :key="company.id" />
</ElSelect>
但这种方式需要将 v-model 中的数据改为完整对象数组 [Company],并维护一致性,略显复杂。
2、取消 remote 模式,前端本地过滤(适用于小数据量)
如果公司列表固定不大,可以一次性加载全部数据,使用 filterable 本地过滤:
<ElSelect filterable :filter-method="customFilter" />