I'm trying to make an option list with v-for
, where you can choose only one option at a time. It works great except I can't unselect the option.
<div id="main">
<ul>
<li
v-for="l in list"
id="l.key"
@click="selectone(l.key, l.isSelected)"
v-bind:class="{ selected : l.isSelected, notselected : !l.isSelected }"
> {{ l.tec }} </li>
<ul>
</div>
The JS
new Vue({
el:"#main",
data: {
list: [
{key:"0", tec:"html", isSelected:false},
{key:"1", tec:"css", isSelected:false},
{key:"2", tec:"JS", isSelected:false},
{key:"3", tec:"Git", isSelected:false},
{key:"4", tec:"NodeJS", isSelected:false},
{key:"5", tec:"Postgres", isSelected:false}
]
},
methods: {
selectone: function(k, o) {
for( i = 0; i < this.list.length; i ++ ) {
if(this.list[i].isSelected == true ) {
this.list[i].isSelected = false
}
}
this.list[k].isSelected = !this.list[k].isSelected;
}
}
})
CSS
.selected {
background:lightpink;
}
.notselected {
background:lightblue;
}
Shouldn't my loop deactivate all options every time I click an element?
In selectone()
, you're setting isSelected=false
for all list items, and then attempting to toggle the selected list item's isSelected
, which was just previously set to false
(i.e., the "toggle" would always set isSelected=true
for the selected item).
The loop should exclude the key of the selected item:
selectone(key) {
for (let i = 0; i < this.list.length; i++) {
if (this.list[i].key !== key) {
this.list[i].isSelected = false
}
}
// this.toggleSelection(key)
}
But the toggling code itself needs a bug-fix to properly lookup the list item. The first argument to selectone()
is the key
property of the list item. In order to get the item by key from the list
array, you have to search the list
, e.g., using Array.prototype.find()
:
toggleSelection(key) {
const listItem = this.list.find(item => item.key === key)
if (listItem) {
listItem.isSelected = !listItem.isSelected
}
}
new Vue({
el: '#app',
data: {
list: [
{key:"0", tec:"html", isSelected:false},
{key:"1", tec:"css", isSelected:false},
{key:"2", tec:"JS", isSelected:false},
{key:"3", tec:"Git", isSelected:false},
{key:"4", tec:"NodeJS", isSelected:false},
{key:"5", tec:"Postgres", isSelected:false}
]
},
methods: {
selectone(key) {
for (let i = 0; i < this.list.length; i++) {
if (this.list[i].key !== key) {
this.list[i].isSelected = false
}
}
this.toggleSelection(key)
},
toggleSelection(key) {
const listItem = this.list.find(item => item.key === key)
if (listItem) {
listItem.isSelected = !listItem.isSelected
}
}
}
})
.selected {
background:lightpink;
}
.notselected {
background:lightblue;
}
<script src="https://unpkg.com/vue@2.6.10"></script>
<div id="app">
<ul>
<li
v-for="l in list"
id="l.key"
@click="selectone(l.key, l.isSelected)"
v-bind:class="{ selected : l.isSelected, notselected : !l.isSelected }"
> {{ l.tec }} </li>
<ul>
</div>
Alternatively, you could track the selected index, set it in the item's click
-handler, and set the class
binding based on the item's index matching the selected index:
// template
<li
v-for="(l, index) in list"
id="l.key"
@click="selectedIndex = index"
v-bind:class="{ selected: index === selectedIndex, notselected: index !== selectedIndex }"
> {{ l.tec }} </li>
// script
export default {
data() {
return {
selectedIndex: -1,
...
}
}
}
new Vue({
el: '#app',
data: {
selectedIndex: -1,
list: [
{key:"0", tec:"html", isSelected:false},
{key:"1", tec:"css", isSelected:false},
{key:"2", tec:"JS", isSelected:false},
{key:"3", tec:"Git", isSelected:false},
{key:"4", tec:"NodeJS", isSelected:false},
{key:"5", tec:"Postgres", isSelected:false}
]
}
})
.selected {
background:lightpink;
}
.notselected {
background:lightblue;
}
<script src="https://unpkg.com/vue@2.6.10"></script>
<div id="app">
<ul>
<li
v-for="(l, index) in list"
id="l.key"
@click="selectedIndex = index"
v-bind:class="{ selected : index === selectedIndex, notselected : index !== selectedIndex }"
> {{ l.tec }} </li>
<ul>
</div>