Tutorial

How To Iterate Over Items in Vue.js With V-for

Vue.js

Introduction

A common requirement for front-end applications is listing out items. It can take the form of a to-do list and card systems. Vue.js supports rendering lists of items onto the browser using the built-in v-for< core directive.

In this post, we will explore how v-for can be used in Vue applications.

Prerequisites

This post assumes you have some knowledge of loops, arrays, and objects in JavaScript. You can refer to this series if you’re getting started with JavaScript.

This exploration will build upon an HTML file that uses a CDN (content delivery network) hosted copy of the Vue.js framework:

vfor.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>v-for</title>
  </head>

  <body>

    <div id="app">
      <h1>{{ message }}</h1>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>

    <script>
      new Vue({
        el: "#app",
        data() {
          return {
            message: "hello"
          }
        }
      });
    </script>

  </body>
</html>

You can refer to this post if you’re getting started with Vue.js.

At this point, if you were to upload this code and view this file in a browser, you will see the message: "hello".

Using v-for with Range

The built-in v-for directive allows us to loop through items.

We can use a range in the v-for directive to iterate a specified number of times.

Let’s replace the contents of our <div> with an unordered list that repeats list items 15 times:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in 15">{{ item }}</li>
  </ul>
</div>

This will result in an unordered list with numbers 1 to 15.

Using the <template> Element

The v-for directive only applies to the element that it’s attached to. If multiple items should repeat with the v-for directive, we should wrap the elements in a <template> element.

Let’s replace the contents of our <div> with a <template>:

vfor.html
<div id="app">
  <template v-for="item in 15">
    <span>{{ item }}</span>
    <button>Count</button>
  </template>
</div>

This will result in a collection of repeating <span>s and <button>s.

Using v-for with Objects

We can loop over the values in an objectItems object from the data model. This can be accomplished by adding the v-for directive in the element that should be repeated.

Let’s modify the lines in data() so it returns an objectItems object:

vfor.html
<script>
new Vue({
  el: "#app",
  data() {
    return {
      objectItems: {
        key1: 'item1',
        key2: 'item2',
        key3: 'item3'
      }
    }
  }
});
</script>

Let’s replace the contents of our <div> with an unordered list that repeats list items:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in objectItems">{{ item }}</li>
  </ul>
</div>

Here’s how things will look:

Output
<div id="app"> <ul> <li>item1</li> <li>item2</li> <li>item3</li> </ul> </div>

This will result in an unordered list with the property values from the object.

In addition to the property value, we get two additional parameters when looping over objects with Vue. Namely, the key and the index values.

The key value gives us access to the current properties key.

The index provides us the index of the current item in the loop. This is the position of the item in the looped list.

Let’s replace the contents of our <div> with an unordered list that repeats list items with item, key, and index:

vfor.html
<div id="app">
  <ul>
    <li v-for="(item, key, index) in objectItems">
      {{ item }} - {{ key }} - {{ index }}
    </li>
  </ul>
</div>

Here’s how things will look:

Output
<div id="app"> <ul> <li>item1 - key1 - 1</li> <li>item2 - key2 - 2</li> <li>item3 - key3 - 3</li> </ul> </div>

This will result in an unordered list with the item, key, and index.

Using v-for with Arrays

We can loop over the items in a shoppingItems array from the data model. This can be accomplished by adding the v-for directive in the element that should be repeated.

Let’s modify the lines in data() so it returns a shoppingItems array with objects:

vfor.html
<script>
new Vue({
  el: "#app",
  data() {
    return {
      objectItems: {
        key1: 'item1',
        key2: 'item2',
        key3: 'item3'
      },
      shoppingItems: [
        { name: 'apple', price: '7' },
        { name: 'orange', price: '12' }
      ]
    }
  }
});
</script>

We can loop over the objects in the shoppingItems array and access the values using a given key.

Let’s replace the contents of our <div> with an unordered list that repeats list items with item.name and item.price:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in shoppingItems">
      {{ item.name }} - {{ item.price }}
    </li>
  </ul>
</div>

Here’s how things will look:

Output
<div id="app"> <ul> <li>apple - 7</li> <li>orange - 12</li> </ul> </div>

This will result in an unordered list with the values from the array.

Using v-bind:key to Track Elements

When the array order is changed, by default Vue would change the data in each existing element rather than moving the DOM elements to the updated position.

We can set Vue to track each element using a key. This would cause it to move elements rather than replacing values.

This value should be unique to each element that’s being iterated over.

Let’s assign a key using item.name:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in shoppingItems" v-bind:key="item.name">
      {{ item.name }} - {{ item.price }}
    </li>
  </ul>
</div>

Now, Vue has a way of tracking a node’s identity as changes are made.

Managing Changes

Out of the box, v-for supports array mutation methods. These are push, pop, shift, unshift, splice, sort and reverse. If any of these operations are performed on an array, the v-for directive updates the view with the new data.

Also, when we replace an array with a new array, Vue finds the most optimized way to update the items.

Problems with Change Management

The two things that Vue cannot track when changed in an array are:

  1. Setting items directly.

Example:

data.shoppingItems[3] = { price: 10, name: 'pineapple' };

This can be resolved by using the Vue.set method. This method accepts the array, an index, and the new value.

Vue.set(data.shoppingItems, 3, { price: 10, name: 'pineapple' });

Also, we can use splice to set the value at a given index.

  1. Modifying array length.

Example:

data.shoppingItems.length = 2;

We can use splice to modify the array length instead of setting it directly to avoid issues.

Filtering Lists

We can filter out lists that are being displayed without changing the original list. This can be done using computed values or by having a method and passing values through when setting the v-for values.

Using computed Values to Filter Items

Instead of setting the value directly on the list, we instead point to a computed value. We can write the logic for filtering out data in that computed function.

First, let’s define itemsLessThanTen:

vfor.html
<script>
new Vue({
  el: "#app",
  data() {
    return {
      objectItems: {
        key1: 'item1',
        key2: 'item2',
        key3: 'item3'
      },
      shoppingItems: [
        { name: 'apple', price: '7' },
        { name: 'orange', price: '12' }
      ]
    }
  },
  computed: {
    itemsLessThanTen: function() {
      return this.shoppingItems.filter(function(item) {
        return item.price < 10;
      })
    }
  }
});
</script>

Then, let’s modify the v-for to use itemsLessThanTen:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in itemsLessThanTen" v-bind:key="item.name">
      {{ item.name }} - {{ item.price }}
    </li>
  </ul>
</div>

Here’s how things will look:

Output
<div id="app"> <ul> <li>apple - 7</li> </ul> </div>

The itemLessThanTen function uses the JavaScript filter function to return any item with a price is less than 10.

Using a Method to Filter Items

In this approach, we pass in the same shoppingItems list directly to a method that we define. By doing so, all the items in the array would be filtered according to the method definition.

First, let’s define filterItems:

vfor.html
<script>
new Vue({
  el: "#app",
  data() {
    return {
      objectItems: {
        key1: 'item1',
        key2: 'item2',
        key3: 'item3'
      },
      shoppingItems: [
        { name: 'apple', price: '7' },
        { name: 'orange', price: '12' }
      ]
    }
  },
  computed: {
    itemsLessThanTen: function() {
      return this.shoppingItems.filter(function(item) {
        return item.price < 10;
      })
    }
  },
  methods: {
    filterItems: function(items) {
      return items.filter(function(item) {
        return item.price < 10;
      })
    }
  }
});
</script>

Then, let’s modify the v-for to use filterItems:

vfor.html
<div id="app">
  <ul>
    <li v-for="item in filterItems(shoppingItems)" v-bind:key="item.name">
      {{ item.name }} - {{ item.price }}
    </li>
  </ul>
</div>

Here’s how things will look:

Output
<div id="app"> <ul> <li>apple - 7</li> </ul> </div>

The filterItems method performs the same function as in the computed value example, and the final result would be the same.

Conclusion

In this article, you learned about using v-for in Vue applications. You were introduced to ranges, keys, computed, and methods.

If you’d like to learn more about Vue.js, check out our Vue.js topic page for exercises and programming projects.

Creative Commons License