ElementPlus 单选框取消选择方案
前言
Element Plus 的单选框默认无法取消已选中的选择,有些业务场景下,不做选择是有意义的,如何才能取消已选中的选择?
方案
方案一:拦截 Radio 组件点击事件,手动更新 Radio Group 的取值
点击已选择的 Radio 取消选择
在线演示:
vue
<template>
<el-radio-group v-model="radio">
<el-radio
v-for="o in list"
:key="o.value"
v-bind="o"
@click.prevent="handleRadioClick(o.value)"
/>
</el-radio-group>
</template>
<script setup>
import { ref } from 'vue'
const radio = ref('')
const list = [
{ value: 'beijing', label: '北京' },
{ value: 'shanghai', label: '上海' },
{ value: 'shenzhen', label: '深圳' },
]
function handleRadioClick(val) {
radio.value = radio.value === val ? null : val
}
</script>方案二:新增一个空白 Radio
实际上没有“取消选择”,而是新增一个空白 Radio 标识“不选择”
vue
<template>
<el-radio-group v-model="radio">
<el-radio v-for="o in list" :key="o.value" v-bind="o" />
<el-radio value="" label="空" />
</el-radio-group>
</template>
<script setup>
import { ref } from 'vue'
const radio = ref('')
const list = [
{ value: 'beijing', label: '北京' },
{ value: 'shanghai', label: '上海' },
{ value: 'shenzhen', label: '深圳' },
]
</script>对比
虽然方案二在视觉上增加了一个看似“冗余”的选项,但在交互设计和代码维护上,它才是更成熟的解决方案。
方案一(点击反选)的局限性:
- 违背用户心智模型:单选框的语义是“互斥且必选其一”,默认 Radio 点击后无法取消,用户需要“猜”或“试”才知道这个 Radio 可以反选
- 状态表达含糊:如果业务允许“不选”,那么“空值”本身就是一个合法的业务状态。将这个状态隐藏在“未选中”的视觉表现中,不如将其显式化更为直观
方案二(显式选项)的优势:
- 交互的确定性:新增“不适用/无/空”选项,明确地告诉用户:“不选择”也是一种有效的选择,这符合系统状态可见性原则。
- 数据流清晰:从数据层面看,Radio Group 应当始终映射一个确定的值。比通过 Hack 点击事件来置空数据,逻辑更加顺畅且易于维护。
综上,建议采取第二种方案。
如果业务场景确实需要“可取消”且不希望“空”选项,可能这里就不该用单选框,更规范的做法是改用 Select 选择器(带清除功能)。
替换方案
带清除功能的选择器
vue
<template>
<el-select v-model="select" clearable>
<el-option v-for="o in list" :key="o.value" v-bind="o" />
</el-select>
</template>
<script setup>
import { ref } from 'vue'
const select = ref('')
const list = [
{ value: 'beijing', label: '北京' },
{ value: 'shanghai', label: '上海' },
{ value: 'shenzhen', label: '深圳' },
]
</script>重置表单
如果单选框是表单的一部分,且存在“重置表单”的按钮,也可以重置表单作为取消单选的辅助方案。虽然交互上比较糟糕,但也不违反用户直觉
vue
<template>
<el-form ref="formRef" :model="form" label-width="auto">
<el-form-item label="城市" prop="radio">
<el-radio-group v-model="form.radio">
<el-radio v-for="o in list" :key="o.value" v-bind="o" />
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="() => formRef?.resetFields()">Reset</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref } from 'vue'
const form = reactive({ radio: '' })
const formRef = ref()
const list = [
{ value: 'beijing', label: '北京' },
{ value: 'shanghai', label: '上海' },
{ value: 'shenzhen', label: '深圳' },
]
</script>