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