Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at Vue’s array change detection capabilities.
Array Change Detection
Vue wraps the following array mutation methods so that when these methods are called, a view update will be triggered. They’re the following:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
For example, if we have a button that triggers a push of a new item to an array, it’ll update:
src/index.js
:
new Vue({
el: "#app",
data: {
persons: [{ name: "Joe" }, { name: "Jane" }]
},
methods: {
addPerson() {
this.persons.push({ name: "Mary" });
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<button @click="addPerson">Add Person</button>
<div v-for="person in persons">
{{person.name}}
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then we get:
JoeJane
When the array is first rendered. Then when we click Add Button, we get:
JoeJaneMary
since we called the addPerson
method with the button click, which called the push
method to add a new entry.
Replacing an Array
Non-mutating array method are ones that always return a new array. We can replace the original array with the returned array to trigger a view update.
Vue doesn’t re-render the list from scratch when we update the array. Instead, it’ll try to maximum DOM element reuse.
For example, if we have the following:
src/index.js
:
new Vue({
el: "#app",
data: {
persons: [{ name: "Joe" }, { name: "Jane" }, { name: "Mary" }]
},
methods: {
filterPerson() {
this.persons = this.persons.filter(p => p.name !== "Joe");
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js)"></script>
</head> <body>
<div id="app">
<button @click="filterPerson">Filter Person</button>
<div v-for="person in persons">
{{person.name}}
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then when our page first loads, we get the following names:
JoeJaneMary
Once we click Filter Person, we get:
JaneMary
since we called the filterPerson
method with the button click, which called the filter
method to return an array with the filtered items.
Then the new array is assigned to this.persons
and then the view is refreshed.
Catches
Vue can’t detect directly assign an item to an array by setting the index or when the length of the array is modified.
So:
vm.item[indexOfItem] = 'item';
and:
vm.items.length = 2;
won’t be picked up by Vue and update the view with updated data.
We can either call Vue.set
or splice
to set an entry to a given index.
Vue.set
For example, we can write the following code:
src/index.js
new Vue({
el: "#app",
data: {
persons: ["Joe", "Mary"]
},
methods: {
addPerson() {
Vue.set(this.persons, 5, "Jane");
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js)"></script>
</head> <body>
<div id="app">
<button @click="addPerson">Add Person</button>
<div v-for="(person, index) in persons">
{{index}} - {{person}}
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
When the page first loads, we get the following entries rendered:
0 - Joe1 - Mary
Then when we click Add Person, we get:
0 - Joe1 - Mary2 -3 -4 -5 - Jane
Splice
We can do the same with splice
, but the length has to be set first:
src/index.js
:
new Vue({
el: "#app",
data: {
persons: ["Joe", "Mary"]
},
methods: {
addPerson() {
this.persons.length = 5;
this.persons.splice(5, 1, "Jane");
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<button @click="addPerson">Add Person</button>
<div v-for="(person, index) in persons">
{{index}} - {{person}}
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Then we get the same results as before.
We can also use splice
to set the length of the array and trigger view update. To do this, we can write:
this.persons.splice(5);
vm.$set
We can call vm.$set
as follows, which is the same as Vue.set
except that it’s available to the Vue instance instead of a global object.
For example, we can write:
src/index.js
:
new Vue({
el: "#app",
data: {
persons: ["Joe", "Mary"]
},
methods: {
addPerson() {
this.$set(this.persons, 5, "Jane");
}
}
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<button @click="addPerson">Add Person</button>
<div v-for="(person, index) in persons">
{{index}} - {{person}}
</div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Conclusion
Some array changes will trigger view updates automatically.
Array mutation method calls are watched to update the view.
Array methods that return new arrays have to treated differently from mutation methods. The returned value has to be assigned to the original variable to trigger a view update.
To trigger view update when we assign an entry to an array index, we can use splice
, Vue.set
, or this.$set
/ vm.$set.
To set the array’s length and trigger view updates, we can also call splice
.