Implementing Reactive Radio Button Binding in Vue.js with TypeScript
This challenge focuses on creating a reusable component in Vue.js that provides reactive binding for radio buttons. Radio buttons are commonly used for selecting a single option from a set, and this component will ensure that the selected value is consistently updated in the parent component and vice versa. This is a fundamental pattern for form handling and user input in Vue applications.
Problem Description
You are tasked with building a RadioGroup component in Vue.js using TypeScript. This component will accept an array of radio button options, each with a value and a label. The component should maintain an internal modelValue representing the currently selected radio button's value. Crucially, the component must emit an update:modelValue event whenever the selected radio button changes, allowing the parent component to react to the selection. The component should also react to changes in the modelValue prop, updating the selected radio button accordingly.
Key Requirements:
- Props:
modelValue: (string | number | null) - The currently selected value.options: (Array<{ value: string | number; label: string }>) - An array of radio button options.
- Events:
update:modelValue: (string | number) - Emitted when the selected radio button changes.
- Reactivity: The component must be reactive to both prop changes (
modelValue) and user interactions (radio button clicks). - Accessibility: Ensure the radio buttons are accessible (e.g., proper labels, ARIA attributes).
- Type Safety: Leverage TypeScript to ensure type safety throughout the component.
Expected Behavior:
- When the component is initialized with a
modelValue, the corresponding radio button should be pre-selected. - When a radio button is clicked, the
update:modelValueevent should be emitted with the selected value. - When the
modelValueprop changes, the corresponding radio button should be selected. - If
modelValueisnullorundefined, no radio button should be selected. - The component should render all provided options as radio buttons with their respective labels.
Edge Cases to Consider:
- Empty
optionsarray. modelValuenot present in any of theoptionsvalues.optionsarray containing duplicatevalues (should be handled gracefully, potentially by only selecting the first matching option).modelValuebeing a type that doesn't match thevaluetype of the options (e.g., stringmodelValueand numberoptions.value). Consider type coercion or throwing an error.
Examples
Example 1:
Input:
options: [{ value: 'A', label: 'Option A' }, { value: 'B', label: 'Option B' }]
modelValue: 'B'
Output:
Radio button with value 'B' and label 'Option B' is selected. Radio button with value 'A' and label 'Option A' is unselected.
Explanation: The component initializes with 'B' as the selected value, so the corresponding radio button is pre-selected.
Example 2:
Input:
options: [{ value: 1, label: 'One' }, { value: 2, label: 'Two' }, { value: 3, label: 'Three' }]
modelValue: null
Output:
No radio button is selected.
Explanation: The component initializes with a null modelValue, so no radio button is selected.
Example 3:
Input:
options: [{ value: 'X', label: 'X' }, { value: 'Y', label: 'Y' }]
modelValue: 'Z'
Output:
No radio button is selected.
Explanation: The modelValue 'Z' does not match any of the option values, so no radio button is selected.
Constraints
- The component must be written in Vue 3 with TypeScript.
- The component should be reusable and easily integrated into other Vue components.
- The component should be reasonably performant, even with a large number of options (e.g., avoid unnecessary re-renders).
- The
optionsarray should not be mutated directly within the component. Changes to the parent's options array should be reflected in the component.
Notes
- Consider using
v-modelfor two-way binding with the parent component. - Think about how to handle different data types for
modelValueandoptions.value. Type coercion might be appropriate, or you could throw an error if the types don't match. - Pay close attention to the
update:modelValueevent and how it interacts withv-model. - Focus on creating a clean, well-documented, and testable component.
- Accessibility is important - ensure your radio buttons are usable by everyone.