Transfer穿梭框选中项无法双向绑定是怎么回事?

ლ明轩 阅读 15

我用Element Plus的Transfer组件做权限分配,明明设置了v-model绑定selectedKeys,但右边已选列表变了,selectedKeys却没更新,控制台打印还是空数组。是我哪里写错了吗?

这是我的代码:

<el-transfer
  v-model="selectedKeys"
  :data="roleOptions"
  :props="{ key: 'id', label: 'name' }"
/>

roleOptions是正常的数据源,selectedKeys初始化为[],但操作后它始终不变。

我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
西门育柯
试试这个:Element Plus 的 Transfer 组件 v-model 绑定的值必须是响应式引用类型(比如 ref 或 reactive),你是不是直接用的 let selectedKeys = []?改成 const selectedKeys = ref([]) 就好了。

如果用的是 Options API,那得写成 data() { return { selectedKeys: [] } },别用 data: { selectedKeys: [] } 这种错写法。

再检查下有没有在模板里漏写 v-model 的修饰符,比如 .lazy,别加那个,Transfer 必须用默认的同步更新。
点赞 5
2026-02-25 19:13
Prog.会静
原理是这样:Element Plus 的 Transfer 组件默认是单向绑定的,它只会在你操作时更新内部状态,但如果你的 v-model 没有正确配合 change 事件,或者数据源结构不匹配,就会出现“界面变了,但绑定值没更新”的情况。

你现在的代码只写了 v-model="selectedKeys",但没监听 change 事件,这是最常见的坑。Element Plus 的 Transfer 并不会自动把选中项同步回 v-model 绑定的变量——它需要你主动通过 change 事件接收新值并赋值回去。

不过等等,先别急着加 @change,我们先确认几个关键点,一步步排查:



第一:检查 roleOptions 里的 id 是否是字符串类型?
Element Plus 的 Transfer 内部比较 key 的时候,用的是严格相等(===),如果你的 id 是数字类型(比如 1, 2, 3),而 v-model 绑定的是字符串数组(比如 ["1", "2"]),它会认为“没选中”,导致双向绑定失效。

✅ 建议统一用字符串类型的 id,哪怕后端返回的是数字,前端也转换一下:

roleOptions: [
{ id: '1', name: '用户管理' },
{ id: '2', name: '角色分配' },
{ id: '3', name: '系统设置' }
]


或者在拿到数据后做一次转换:

this.roleOptions = rawList.map(item => ({
id: String(item.id),
name: item.name
}))




第二:加上 @change 事件,手动同步值。
Transfer 的 change 事件签名是 (value: string[] | number[], direction: 'left' | 'right', movedKeys: string[] | number[]),其中第一个参数 value 就是当前所有已选中的 key,你得把它赋给 selectedKeys

✅ 正确写法是这样的:

<el-transfer
v-model="selectedKeys"
:data="roleOptions"
:props="{ key: 'id', label: 'name' }"
@change="handleTransferChange"
/>


export default {
data() {
return {
selectedKeys: [], // 注意:初始化必须是数组
roleOptions: [
{ id: '1', name: '用户管理' },
{ id: '2', name: '角色分配' },
{ id: '3', name: '系统设置' }
]
}
},
methods: {
handleTransferChange(value, direction, movedKeys) {
// 这里 value 就是当前 Transfer 中右侧已选中的 key 列表
// 必须手动赋值!
this.selectedKeys = value
console.log('当前已选中:', this.selectedKeys)
}
}
}




第三:别忘了检查 v-model:data 的配合逻辑。
如果 roleOptions 里某个 idundefinednull,Transfer 会忽略它,但不会报错,结果就是你选了,但 selectedKeys 里没它。
你可以在控制台打印下 roleOptions 的前几项确认:

console.log('roleOptions sample:', this.roleOptions.slice(0, 3))




最后补充一个容易被忽略的点:
如果你用了 filterable(搜索功能),Transfer 会把搜索框的值也参与 key 匹配,导致选中异常。如果不需要搜索,建议先关掉 filterable 排除干扰:

<!-- 先别开 filterable -->
<el-transfer
v-model="selectedKeys"
:data="roleOptions"
:props="{ key: 'id', label: 'name' }"
@change="handleTransferChange"
/>




我之前踩过这个坑,就是以为 v-model 是自动双向的,结果发现 Transfer 根本没触发响应式更新——它依赖你手动同步。Element Plus 的很多组件都是这种“给事件你,自己处理”的风格,不是像 Vue 自带的 input 那样自动同步。

你按上面三步改完,应该就能用了。如果还是不行,把 roleOptions 的结构贴出来,我帮你看看是不是 id 类型或者嵌套问题。
点赞 2
2026-02-25 15:00