[提问] 如何自定义 radio 和 radio-group 后

继上次提问后
https://forum.vuejs.org/t/checkbox-checkbox-group/133956/3
我修改了下我的 RadioRadioGroup 代码,测试的时候发现

<template>
  <div class="radio-views">
    <RadioGroup v-model="selected">
      <Radio value="hello">Option A</Radio>
      <Radio value="world">Option B</Radio>
      <Radio value="fuck">Option C</Radio>
    </RadioGroup>

    <RadioGroup v-model="selected">
      <Radio value="hello" disabled>Option A</Radio>
      <Radio value="world">Option B</Radio>
      <Radio value="fuck">Option C</Radio>
    </RadioGroup>
  </div>
</template>

<script lang="ts" setup>
const selected = ref("hello")

image

  1. radio 不能单选
  2. 不能同步更新
  3. 而且调试的时候发现
const ischecked = computed(() => {
  console.log(`selected: ${selected.value}, props.value: ${props.value}, modevalue: ${props.modelValue}`)
  return selected.value === props.value
})


传入的值都变成 false

测试单独的 Radio 的时候没有这个上述问题

<template>
    <Radio value="hello" v-model="selected3" label="hello"/>
    <Radio value="world" v-model="selected3" label="world"/>
    <Radio value="fuck" v-model="selected3" label="fuck"/>

    <Radio value="hello" v-model="selected3" label="hello"/>
    <Radio value="world" v-model="selected3" label="world"/>
    <Radio value="fuck" v-model="selected3" label="fuck"/>
</template>
<script lang="ts" setup>
const selected3 = ref("")
</script>

image


这是我的相关代码
Radio.vue

<template>
  <label class="radio">
    <input type="radio" v-model="selected" :value="value"
           :checked="ischecked"
           @change="updateInput($event)"
           :disabled="disabled">
    <span>
      <slot/>
      <template v-if="!$slots.default">{{label}}</template>
    </span>
  </label>
</template>

<script lang="ts" setup>
import {computed, inject, ref, WritableComputedRef} from "vue";

const props = defineProps({
  modelValue: {
    type: [String, Number, Boolean, Object]
  },

  value: {
    type: [String, Number, Boolean, Object],
    required: true
  },

  label: String,
  disabled: {
    type: Boolean,
    default: false
  }
})
const emits = defineEmits(["update:modelValue"])
const selected = props.modelValue !== undefined ? computed({
  get: () => props.modelValue,
  set: (newvalue) => emits("update:modelValue", newvalue)
}) : inject<WritableComputedRef<any>>("selected")!
const ischecked = computed(() => {
  console.log(`selected: ${selected.value}, props.value: ${props.value}, modevalue: ${props.modelValue}`)
  return selected.value === props.value
})

const updateInput = (event: {target: any}) => {
  selected.value = event.target.value
}
</script>

<style lang="scss" scoped>
label.radio {
  display: inline-block;
}
</style>

RadioGroup.vue

<template>
  <div class="radio-group">
    <slot/>
  </div>
</template>

<script lang="ts" setup>
import {computed, provide} from "vue";

const props = defineProps<{
  modelValue: any,
}>()

const emits = defineEmits(["update:modelValue"])
const selected = computed({
  get: () => props.modelValue,
  set: (newvalue) => emits("update:modelValue", newvalue)
})
provide("selected", selected)

</script>

<style scoped>

</style>

抱歉打扰下 @wei_ba , 我可能需要您的意见

radio.vue modelValue 因支持boolean导致默认值为false 因此未使用group的值 ,思路及改法等东西后面再编辑 mark一下

先把关键的地方贴出来 Props | Vue.js (vuejs.org)

根据文档所说,Boolean 默认会是 false,并且支持多种类型的情况下也会优先应用

定位问题思路

  1. 你所说的不能同步更新可以直接断定各组件所需的值与原始传入的modelValue是无关的
  2. 传入的值变成了false,就意味着传入的值被覆盖
检查传递的值再何时不再是原始的值,在个组件出调试modelValue与原始值进行比较,可以得知在Radio.vue文件里的数据对不上
检查inject是否正确执行

Radio.vue

const selected = props.modelValue !== undefined ? computed({
  get: () => props.modelValue,
  set: (newvalue) => emits("update:modelValue", newvalue)
}) : inject<WritableComputedRef<any>>("selected")! //最好不要这样做,没有传递`modelValue`且不被`RadioGroup`包裹会其他逻辑默认有值的话会出错

发现props.modelValue !== undefined的值为true,就意味着props.modelValue是非undefined,直接输出props.modelVaue发现值就是false,未传递值那就是默认值的问题,原因开头的文档链接里有解释

问题分析完成开始解决,最简单的办法就是直接给modelValue设置默认值为void 0 //undefined
更好一点的就是不依靠modelValue的默认值来判断

依然的随口一提
  • 最好用inject来判断是否被包裹,inject(…,//这个是没有provide的默认值,不填为undefined,也最好显式填写一下)
  • 你重复的东西写太多了,比如Radio.vue中的v-mode,:checked,@change,都在反复做一件事