Reputation: 139
When the array shows[] changes after I click the button(I can see the shows[] changes in the Vue's chrome plugin), character 'a' is still on the page.'b' and 'c' never show up.
<script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
currentShow:0,
shows:[true, false, false],
},
watch:{
currentShow: function (val, old) {
this.shows[old] = false;
this.shows[val] = true
}
}
});
</script>
<!-- Letter 'a', 'b', 'c' shows only when its corresponding shows[] is true-->
<div id="app">
<p v-show="shows[0]">a</p>
<p v-show="shows[1]">b</p>
<p v-show="shows[2]">c</p>
<button @click="currentShow=(currentShow+1)%3">next</button>
</div>
Upvotes: 2
Views: 7312
Reputation: 399
Vue cannot watch on array elements if they are primitives (not objects typically). Vue updated is triggered by push()
, pop()
and other methods but it cannot know when you update a value inside. See the docs.
I found a specific solution for your example, where it can only be one selected item, feel free to adapt it.
let vm = new Vue({
el: '#app',
data: {
currentShow: 0
},
methods: {
showNext(){
this.currentShow = (this.currentShow+1) % 3
}
}
});
<script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<!-- Letter 'a', 'b', 'c' shows only when its corresponding shows[] is true-->
<div id="app">
<p v-show="currentShow==0">a</p>
<p v-show="currentShow==1">b</p>
<p v-show="currentShow==2">c</p>
<button @click="showNext()">next</button>
</div>
Upvotes: 0
Reputation: 135742
That is a reactivity caveat. You could use Vue.set()
:
<script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vue.js"></script>
<!-- Letter 'a', 'b', 'c' shows only when its corresponding shows[] is true-->
<div id="app">
<p v-show="shows[0]">a</p>
<p v-show="shows[1]">b</p>
<p v-show="shows[2]">c</p>
<button @click="currentShow=(currentShow+1)%3">next</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
currentShow: 0,
shows:[true, false, false],
},
watch:{
currentShow: function (val, old) {
Vue.set(this.shows, old, false);
Vue.set(this.shows, val, true);
}
}
});
</script>
Docs:
If you add or edit elements by index, you have to call Vue.set()
:
Vue.set(this.shows, old, false);
Vue.set(this.shows, val, true);
Or:
this.shows.splice(old, 1, false);
this.shows.splice(val, 1, true);
This enables Vue to adjust the reactivity to that element.
Besides regular caveat problems, the docs have a specific guidance on arrays:
Caveats
Due to limitations in JavaScript, Vue cannot detect the following changes to an array:
- When you directly set an item with the index, e.g.
vm.items[indexOfItem] = newValue
- When you modify the length of the array, e.g.
vm.items.length = newLength
For example:
var vm = new Vue({ data: { items: ['a', 'b', 'c'] } }) vm.items[1] = 'x' // is NOT reactive vm.items.length = 2 // is NOT reactive
To overcome caveat 1, both of the following will accomplish the same as
vm.items[indexOfItem] = newValue
, but will also trigger state updates in the reactivity system:// Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
You can also use the
vm.$set
instance method, which is an alias for the globalVue.set
:vm.$set(vm.items, indexOfItem, newValue)
To deal with caveat 2, you can use
splice
:vm.items.splice(newLength)
Upvotes: 2