I am trying to make a reuseable vue radio-button component that will take a variable name, and an object containing labels and values, then render a list of radio buttons using v-for.
I have had success with each half of the problem, but have not managed to combine them:
//component
const Radio = {
template: '#test',
prop: ['value'],
data () {
return {
selected: this.value
}
},
model: {
prop: 'value',
event: 'change'
},
methods: {
handleClickInput (e) {
this.$emit('change', this.selected)
}
}
}
//app
var app2 = new Vue({
el: '#app2',
data: {
door: '',
doorOptions: {
'Yes': 1,
'No': 0,
}
},
components: { Radio, }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app2">
<radio v-model="door"></radio>
<p>
door = {{door}}
</p>
</div>
<template id="test">
<div>
<input type="radio" value="0" v-model="selected" @change="handleClickInput">0
<input type="radio" value="1" v-model="selected" @change="handleClickInput">1
</div>
</template>
// component
Vue.component('radio-set', {
template: '#radio-set',
props: {
'label-name': '',
'variable': '',
'options': '',
},
methods: {
clicked: function(variable, key, value) {
// none of this is right, it doesn't update the vue data model
window[variable] = value; //assign the new value to the dynamic variable name
selected = value;
this.$emit("click-event", variable) //create the click event for model updating by the parent
}
},
})
//app
var app = new Vue({
el: '#vueApp',
data: {
door:'initial value',
doorOptions: {
'Yes':1,
'No':0,
'Maybe':5,
'A new option':25
},
},
methods: {
buttonClick: function(p1){
console.log(p1+': '+window[p1]); //the variable was assigned inside the child component
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vueApp">
<radio-set
label-name="Radio button set"
variable="door"
:options="doorOptions"
@click-event="buttonClick"
>door: {{door}}
</radio-set>
</div>
<template id="radio-set">
<div>
<label>{{labelName}}:</label>
<button
type="button"
v-for="(val, key) in options"
@click="clicked(variable, key, val)"
>
{{ key }}
</button>
</div>
</template>
Could anyone help with a couple of pointers on how I could move forwards?
For creating dynamic RadioButton, we need to use android. view. ViewGroup. LayoutParams which configures the width and height of views and implements setOnCheckedChangeListener() method of RadioGroup class.
Radio button is an element that allows the user to select only one option from multiple options. You can check my previous articles on Vue. js from the below mentioned links.
.lazy. By default, v-model syncs the input with the data after each input event (with the exception of IME composition as stated above). You can add the lazy modifier to instead sync after change events: <!-- synced after "change" instead of "input" --> <input v-model.
As @PierreSaid mentioned, you can read more about v-model
usage on custom componet.
This is an other example to use input[type="radio"]
and emit change event back to parent componet.
// component
Vue.component('radio-set', {
template: '#radio-set',
props: {
'label-name': '',
'value': '',
'options': '',
}
})
//app
var app = new Vue({
el: '#vueApp',
data() {
return {
door: null,
doorOptions: {
'Yes': 1,
'No': 0,
'Maybe': 5,
'A new option': 25
}
};
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="vueApp">
<radio-set label-name="Radio button set" v-model="door" :options="doorOptions"></radio-set>
door: {{door}}
</div>
<template id="radio-set">
<div>
<div>{{labelName}}:</div>
<label v-for="(val, key) in options" :key="val">
<input type="radio"
:name="labelName"
:value="val"
:checked="val == value"
@change="$emit('input', val)">
{{ key }}
</label>
</div>
</template>
First of all : For your options it would be easier to have an array.
doorOptions: [
{ key: "Yes", value: 1 },
{ key: "No", value: 0 },
{ key: "Maybe", value: 5 },
{ key: "A new option", value: 25 }
]
};
That way you can iterate over it.
Also a good way to synchronise the selected value between your custom component and your app would be to use v-model.
A tutorial to implement v-model
That way we can create a reusable component like that :
<template>
<div>
<label>{{labelName}}:</label>
<button
type="button"
v-for="(val, idx) in options"
:key="idx"
@click="clicked(val)"
>{{ val.key }}</button>
</div>
</template>
<script>
export default {
props: ["value", "options", "labelName"],
methods: {
clicked(val) {
this.$emit("input", val);
}
}
};
</script>
And use it like this
<template>
<div id="app">
<radio-set v-model="selected" label-name="Radio button set" :options="doorOptions"/>
Selected : {{selected.key}}
</div>
</template>
<script>
import RadioSet from "./components/RadioSet";
export default {
name: "App",
components: {
RadioSet
},
data() {
return {
selected: null,
doorOptions: [
{ key: "Yes", value: 1 },
{ key: "No", value: 0 },
{ key: "Maybe", value: 5 },
{ key: "A new option", value: 25 }
]
};
}
};
</script>
Live demo
Based on one of the other answers, I ended up with this for Vue 3. There are some breaking changes with how v-model
works, namely in the props names. (https://v3-migration.vuejs.org/breaking-changes/v-model.html)
Styling is rudimentary, but I wanted something to indicate what the active selection was.
<template>
<div>
<label>{{ labelName }}:</label>
<button
:class="val.key === modelValue ? 'active' : 'inactive'"
type="button"
v-for="(val, idx) in options"
:key="idx"
@click="clicked(val)"
>
{{ val.key }}
</button>
</div>
</template>
<script>
export default {
props: ["modelValue", "options", "labelName"],
methods: {
clicked(val) {
console.log("emitting click", val.key);
this.$emit("update:modelValue", val.key);
},
}
};
</script>
<style scoped>
button {
padding: 10px;
margin: 5px;
}
.active {
background-color: #42b983;
}
.inactive {
background-color: lightgray;
}
</style>
It gets used in the same way. I just map a basic array to {key, value} since that's all I need, but it would be easy to do something more explicit.
<template>
<radio-set
label-name="On / Off"
v-model="myValue"
:options="options.radioOptions"
></radio-set>
</template>
<script>
import RadioSet from "./RadioSet.vue";
let options = {
radioOptions: ["on", "off"].map((x) => {
return { key: x, val: x };
}),
};
export default {
name: "MyComponent",
components: {
RadioSet,
},
data: () => {
return {
options,
myValue: "unknown",
};
},
};
</script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With