Vue Radio Button Group Binding
This challenge focuses on implementing a common UI pattern in Vue.js: creating a group of radio buttons where only one can be selected at a time, and the selected value is easily accessible and manageable through Vue's data binding. This is a fundamental building block for many forms and interactive elements.
Problem Description
Your task is to create a Vue component that renders a group of radio buttons. This component should allow a parent component to control which radio button is currently selected and to receive updates when the user changes the selection.
Key Requirements:
- Component Structure: Create a Vue component (e.g.,
RadioButtonGroup.vue) that accepts a list of options for the radio buttons and a prop to represent the currently selected value. - Rendering Radio Buttons: The component should dynamically render a set of
<input type="radio">elements based on the provided options. Each radio button should have a uniquevalueand anameattribute (all radio buttons in the group should share the samename). v-modelIntegration: The component must be designed to work seamlessly with Vue'sv-modeldirective. This means it should accept amodelValueprop and emit anupdate:modelValueevent when the selection changes.- Option Representation: Each option should likely be an object containing at least a
label(what the user sees) and avalue(the underlying data associated with the option). - Selected State: The radio button corresponding to the
modelValueprop should be checked. - User Interaction: When a user clicks on a radio button, the component should emit an
update:modelValueevent with thevalueof the newly selected radio button.
Expected Behavior:
When used in a parent component with v-model, the RadioButtonGroup should:
- Display a set of radio buttons.
- Have the radio button whose
valuematches the parent's bound variable pre-selected. - Update the parent's bound variable when the user clicks a different radio button.
Edge Cases:
- What happens if no options are provided?
- What happens if the
modelValueprop is initiallynullorundefined? - Ensure that all radio buttons within a group share the same
nameattribute for proper browser behavior.
Examples
Example 1: Basic Usage
Parent Component (App.vue):
<template>
<div>
<RadioButtonGroup v-model="selectedFruit" :options="fruitOptions" />
<p>Selected Fruit: {{ selectedFruit }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue';
export default defineComponent({
components: {
RadioButtonGroup,
},
setup() {
const selectedFruit = ref('banana'); // Initial selection
const fruitOptions = ref([
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
{ label: 'Cherry', value: 'cherry' },
]);
return {
selectedFruit,
fruitOptions,
};
},
});
</script>
RadioButtonGroup.vue (Conceptual Structure):
<template>
<div v-for="option in options" :key="option.value">
<input
type="radio"
:id="option.value"
:value="option.value"
:name="groupName" // A unique name for this group
:checked="modelValue === option.value"
@change="$emit('update:modelValue', option.value)"
/>
<label :for="option.value">{{ option.label }}</label>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
export default defineComponent({
props: {
modelValue: {
type: [String, Number, null], // Allow for different value types, or no initial selection
required: true,
},
options: {
type: Array,
required: true,
validator: (value: any[]) => value.every(opt => typeof opt.label === 'string' && opt.hasOwnProperty('value')),
},
},
emits: ['update:modelValue'],
setup(props) {
// A generated name to ensure radio buttons are grouped correctly
const groupName = computed(() => `radio-group-${Math.random().toString(36).substring(7)}`);
return {
groupName,
};
},
});
</script>
Output in Parent:
Selected Fruit: banana
When the user clicks "Apple", the output should change to:
Selected Fruit: apple
Example 2: No Initial Selection
Parent Component (App.vue):
<template>
<div>
<RadioButtonGroup v-model="selectedColor" :options="colorOptions" />
<p>Selected Color: {{ selectedColor || 'None' }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue';
export default defineComponent({
components: {
RadioButtonGroup,
},
setup() {
const selectedColor = ref(null); // No initial selection
const colorOptions = ref([
{ label: 'Red', value: 'red' },
{ label: 'Green', value: 'green' },
{ label: 'Blue', value: 'blue' },
]);
return {
selectedColor,
colorOptions,
};
},
});
</script>
Output in Parent:
Selected Color: None
When the user clicks "Green", the output should change to:
Selected Color: green
Constraints
- The
optionsprop will be an array of objects. Each object will have alabel(string) and avalue(string, number, or null). - The
modelValueprop can be a string, number, or null. - The component should render at least one radio button if options are provided.
- Performance is not a critical concern for this challenge, but avoid excessively inefficient rendering or event handling.
Notes
- Consider how to manage the
nameattribute for the radio buttons to ensure they are grouped correctly by the browser. A dynamically generated name or a passed-in prop could work. - Remember that Vue 3 uses the
modelValueprop andupdate:modelValueevent forv-modelby default. - Think about the accessibility of your radio buttons (e.g., using
labelelements correctly withforattributes). - The
validatorin theoptionsprop is a good practice for ensuring data integrity.