If you add elements by index, you have to call Vue.set():
Vue.set(this.test, 3, 3);
Or:
this.test.splice(3, 1, 3)
This enables Vue to adjust the reactivity to that element. Updated codePen.
Why?
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 global Vue.set:
vm.$set(vm.items, indexOfItem, newValue)
To deal with caveat 2, you can use splice:
vm.items.splice(newLength)
Updated stack snippet:
var vm = new Vue({
el: "#app",
data: {
test: [1, 2],
},
methods: {
changeVar1: function() {
this.test[3] = 3;
this.test.push(4);
},
changeVar2: function() {
Vue.set(this.test, 3, 3);
// this.test.splice(3, 1, 3); // would work the same
}
}
})
html * {
box-sizing: border-box;
}
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
padding: 4em;
}
<script src="https://cdn.jsdelivr.net/vue/1.0.26/vue.min.js"></script>
<div id="app">
<button @click="changeVar1()">Work</button>
<button @click="changeVar2()">Didn't work, now fixed</button>
<p v-for="(index, digit) in test">{{digit}}</p>
</div>