Menu Close

List Rendering with Vue.js — Array Change Detection

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 .

Posted in vue