I'm trying to set up a simple Vue instance with a collection of radio buttons. The goal is that, if the user clicks on a radio button that is already checked, it will uncheck the respective radio button. But I couldn't do this using Vue yet. Here's my code so far:
HTML:
<div id="app">
<div v-for="(val, key) in list">
<input type="radio" name="radio" :value="val" v-model="selected" :id="val">
<label :for="val" @click="uncheck( val )">{{ val }}</label>
</div>
<button @click="uncheckAll">Uncheck all</button>
</div>
JS:
var app = new Vue({
el: '#app',
data : {
list: [ 'one', 'two', 'three' ],
selected: 'two',
},
methods : {
uncheck: function( val ){
console.log( val, this.selected );
if ( val == this.selected ){
this.selected = false;
}
},
uncheckAll: function(){
this.selected = false;
}
}
})
Seems like the uncheck method is called, but then the radio button triggers a change event and then updates the value of selected again. uncheckAll method works as expected, probably because it's not tied to the data using v-model.
Any tips or suggestions to make this work? Here's a pen I created for this example: https://codepen.io/diegoliv/pen/JrPBbG
The difficulty is that v-model is changing the value of selected before you can examine it to see what its previous value is. You need another variable.
The idea is: when you click an item, it checks whether it matches previouslySelected (rather than selected). If it matches, unselect. Then set previouslySelected to the value of selected.
The click handler should be on the input, not on the label; it is the function of the label to forward clicks to the input, anyway.
var app = new Vue({
el: '#app',
data: {
list: ['one', 'two', 'three'],
selected: 'two',
previouslySelected: 'two'
},
methods: {
uncheck: function(val) {
if (val === this.previouslySelected) {
this.selected = false;
}
this.previouslySelected = this.selected;
},
uncheckAll: function() {
this.selected = false;
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div v-for="(val, key) in list">
<input type="radio" name="radio" :value="val" v-model="selected" :id="val" @click="uncheck(val)">
<label :for="val">{{ val }}</label>
</div>
<button @click="uncheckAll">Uncheck all</button>
</div>
set to happen. (thanks to Vlad T. in the comments)
A more in-the-data way of approaching it would be to have a computed based on your selected, and have its setter do the check for re-selecting. No click handler. Everything is handled in the normal course of setting the value.
var app = new Vue({
el: '#app',
data: {
list: ['one', 'two', 'three'],
d_selected: 'two'
},
computed: {
selected: {
get() {
return this.d_selected;
},
set(v) {
if (v === this.d_selected) {
this.d_selected = false;
} else {
this.d_selected = v;
}
}
}
},
methods: {
uncheckAll: function() {
this.d_selected = false;
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<div v-for="(val, key) in list">
<input type="radio" name="radio" :value="val" v-model="selected" :id="val">
<label :for="val">{{ val }}</label>
</div>
<button @click="uncheckAll">Uncheck all</button>
</div>
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