Vue.js 3 Iteration Rendering Tutorial

In this Vue tutorial we learn how to render things by iterating through data.

We cover the binding directives, required attributes, objects and arrays, nested elements and conditional iteration.

Lesson Project

If you want to follow along with the examples in this lesson, you will need an app generated by the Vue CLI . If you already have one from the previous lesson, you can use it instead.

What is Iteration Rendering

Vue allows us to control the flow of our application by looping through a data list and rendering elements for each item in the list.

As an example, let’s say we want to create a page that provides details for a list of the latest films. We want each list item to be a card with the film name, description, image and a link to its page.

We don’t want to manually create a card in the template for each film, that’s inefficient. Instead, we want to create a template of what the card should look like, then let Vue render that card for each film in the list, automatically filling in the details from the data.

The v-for iteration directive

The v-for iteration directive allows us to loop through elements in an iterable list, like an array.

The directive expects a valid Javascript for loop expression as its value and is used on the element we want to render.

Syntax: v-for
<element v-for="item in list">
  {{ item }}
</element>

In this case we use a Javascript for..in loop . Let’s understand the syntax.

  • list is our iterable list of items, like an array.
  • item is an alias that refers to the current item in the loop.

The list must have the same name as the list in the data property, but the item can be named whatever we want.

The statement reads as follows: for each 'item' in 'array', show this element

As an example, we’ll create an array with two names and loop through them in a paragraph.

note We also add :key="name" after the v-for , which we’ll explain in the next section. For now, just know it’s required for the application to compile.

Example: src/App.vue
<template>
  <p v-for="name in names" :key="name">
    {{ name }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      names: ['John', 'Jane']
    };
  }
}
</script>

Each time the loop runs, it will create a new paragraph and populate it with the next item in the array where we specify the alias.

The key attribute

When we use v-for , Vue keeps track of the elements it iterates over with an index. When changes are made to the elements, Vue will replace their positions in the DOM according to their indexes, not their values.

This can cause some problems in the behavior of our application, so Vue allows us to bind to the key attribute with v-bind:key (or :key ) to give each element a unique ID.

Syntax: unique key
<element v-for="item in list" :key="unique key">
  {{ item }}
</element>

The Application API requires keys on all elements that use the v-for directive. If we don’t include a key, the application won’t compile.

The Global API doesn’t require keys, but it’s still recommended to use them.

Without keys, Vue uses an algorithm that minimizes element moving. It tries to patch or reuse elements of the same type, in-place, as much as possible.

With keys, Vue’s virtual DOM algoritm can identify nodes with the unique id when diffing the new DOM tree with the old DOM tree.

We should make note of a few things regarding the key’s usage.

  • Although the non-key behavior of patching is more performant, it can lead to problems and bugs in the application.
  • Typical values to provide to a key is a type of ID property. However any unique property, like an email address, can be used as long as it’s not a non-primitive value (like an object or array).
  • The key is used on the same element that the v-for is run on. This includes the template tag.

How to get the index of an element in a v-for

Vue allows us to get the index of each item in our iterable list. When using multiple aliases, we add them in a parameter list separated by a comma.

Syntax: v-for index
<element v-for="(alias, index) in list">
  {{ index }} - {{ alias }}
</element>

We can name it whatever we want, Vue knows that it refers to an index in this case.

To demonstrate, let’s expand our earlier example and add an index.

Example: src/App.vue
<template>
  <p v-for="(name, x) in names" :key="name">
    {{ x }} - {{ name }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      names: ['John', 'Jane']
    }
  }
}
</script>

When we run the example in the browser, we should see a number next to each name.

How to iterate through an array of objects

Many times, the list we want to iterate over will be more complex than array elements. For example, objects returned from an API call.

In that case, the expression in the v-for loop stays the same, but we access the data in each object with dot notation.

To demonstrate, let’s expand our example from an array of elements to an array of objects.

Example: src/App.vue
<template>
  <p v-for="(name, x) in names" :key="name.id">
    {{ x }} - {{ name.first }} {{ name.last }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      names: [
        { id: 0, first: 'John', last: 'Doe' },
        { id: 1, first: 'Jane', last: 'Doe' }
      ]
    };
  }
}
</script>

When we run the example in the browser, it will show both the first and last name in each paragraph.

How to iterate through nested arrays

Sometimes, an application will need to work with data where arrays are nested inside other arrays. To access nested arrays, we use nested loops.

For example, let’s say we have a social application and we want to display each user in a card with the names of their friends. Each user is an object in an array that contains a property with a nested array that contains their friends.

As mentioned before, we need to use nested loops. So let’s start with the outer loop that iterates over the users.

note To make the end result easier to visualize, we add some CSS styling that adds a border around each person.

Example: src/App.vue
<template>
  <div class="card" v-for="person in people" :key="person.id">
    <h2>{{ person.name }}</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      people: [
        { id: 0, name: 'John Doe', friends: ['Scott Lang', 'Tony Stark'] },
        { id: 1, name: 'Jane Doe', friends: ['Hope Van Dyne', 'Pepper Potts'] }
      ]
    }
  }
}
</script>

<style>
.card{width:30rem;margin:1rem auto;padding:.5rem;border:1px solid black}
</style>

When we run the example in the browser, we should see two cards with a border around them, showing the names of the two people.

Now we want to show each person’s friends from the nested array inside their card. To do that we use the person alias from the outer loop to access its properties. Then we access the friends array with dot notation.

Example: src/App.vue
<template>
  <div class="card" v-for="person in people" :key="person.id">
    <h2>{{ person.name }}</h2>
    <p class="card sub" v-for="friend in person.friends" :key="friend">
      {{ friend }}
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      people: [
        { id: 0, name: 'John Doe', friends: ['Scott Lang', 'Tony Stark'] },
        { id: 1, name: 'Jane Doe', friends: ['Hope Van Dyne', 'Pepper Potts'] }
      ]
    }
  }
}
</script>

<style>
.card{width:30rem;margin:1rem auto;padding:.5rem;border:1px solid black}
.sub {width:initial;margin:.5rem}
</style>

When we run the example in the browser, we should see the names of each person’s friends inside their card.

Let’s quickly go over the process again.

The outer loop allows us to access each object in the people array with the person alias. Then we use the alias to access the nested array in an inner loop and iterate over each friend .

Vue processes nested v-for loops hierarchically, which means it will go through the following steps.

  1. Step 1: It will access the name property of the first object in the people array and output it in the string interpolation we specified.
  2. Step 2: It will access the friends property and see that it’s an array too. Before it can move on to the second element in the outer array, it will have to fully process the inner array first.
  3. Step 3: It will access each element in the inner array and create a paragraph for it.
  4. Step 4: Once it’s done with the inner array, it will move on to the second element in the outer array and repeat the process.

How to iterate through a single object

Sometimes, the data we want to iterate over will be stored in a single object. In that case we want to loop over each property just like we do with an array.

As an example, we’ll create an object with 3 properties and iterate it in a paragraph.

Example: src/App.vue
<template>
  <p v-for="value in info" :key="value">
    {{ value }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      info: {
        name:     'John Doe',
        blogName: 'Mistaken Identity',
        url:      'https://doe-re-mi.com'
      }
    }
  }
}
</script>

When we run the example above in the browser, we should see each property displayed as its own paragraph.

How to get the object key in a v-for

When looping through object properties, Vue allows us to get not only the index, but also the property name.

The property name is also known as the “key” because property:value is often referred to as key:value. In this case when we refer to the key, we mean the property name and not the unique identifier key.

We get the key the same way we get a list index, we define all the aliases in a parameter list separated by a comma.

Syntax: v-for object
<element v-for="(alias, key, index) in list">
  {{ index }} - {{ key }}: {{ alias }}
</element>

When working with an object, Vue expects the key as the second parameter and the index as the third.

To demonstrate, let’s change our previous example to include the key and index.

Example: src/App.vue
<template>
  <p v-for="(value, key, i) in info" :key="value">
    {{ i }} - {{ key}}: {{ value }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      info: {
        name:     'John Doe',
        blogName: 'Mistaken Identity',
        url:      'https://do-re-mi.com'
      }
    }
  }
}
</script>

When we run the example above in the browser, we get the property names exactly as they are defined in the info object.

Conditional looping with v-for, v-if and Computed Properties

There will be times where we need to loop through elements in a list and display them conditionally.

As an example, let’s say we want to filter out all the users below the age of 18 in a voting app. The first solution that comes to mind is using the v-for and v-if directives on the same element.

Example:
<element v-for="user in users" v-if="user.age >= 18">
  {{ user.name }}
</element>

Unfortunately this won’t work. Vue executes the v-if directive before the v-for directive so the v-if doesn’t have access to the user alias.

We can use nested elements as a quick solution. So, we can run the v-for loop on an outer wrapper element, then do the actual filtering with v-if on the element that’s going to display the result.

Example: src/App.vue
<template>
  <template v-for="user in users" :key="user.name">
    <p v-if="user.age >= 18">{{ user.name }}</p>
  </template>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { name: 'John', age: 33 },
        { name: 'Jane', age: 33 },
        { name: 'Jack', age: 17 },
        { name: 'Jill', age: 16 }
      ]
    }
  }
}
</script>

This works perfectly fine and is a valid approach. However, the recommended way to do it is with a computed property .

The idea is to have our conditional logic in a computed property, then we can iterate over that computed property instead of the array.

Let’s change our previous example to use a computed property.

We’ll use the Javascript filter method in an arrow function to filter users aged 18 and above. The method will return a new array with the filtered users and store it in legalUsers, which we can then loop over.

Example: src/App.vue
<template>
  <p v-for="user in legalUsers" :key="user.name">
    {{ user.name }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { name: 'John', age: 33 },
        { name: 'Jane', age: 33 },
        { name: 'Jack', age: 17 },
        { name: 'Jill', age: 16 }
      ]
    }
  },
  computed: {
    legalUsers() {
      // for each item, filter it only if the age is >= 18
      // then return a new array with the results
      return this.users.filter(user => user.age >= 18);
    }
  }
}
</script>

When we run the example above, we get the same results as the nested element approach.

But computed properties have one significant advantage, they’re cached. Using them will ensure our application doesn’t take a performance hit when creating something more practical, like a table of data.

tip In our case the filter method was sufficient for our conditional logic because we only wanted to filter the results. If we wanted to mutate the data we would use something like the map method.

How to sort arrays

If we want to sort an array in ascending or descending order, we can use the built-in Javascript sort method.

As an example, let’s consider the array in the previous example that only returns users above the age of 18.

At the moment, it returns the usernames by the order in which they were defined. If we want to sort the names in alphabetical order, we can chain the sort method onto the filter method.

Example: src/App.vue
<template>
  <p v-for="user in legalUsers" :key="user.name">
    {{ user.name }}
  </p>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { name: 'John', age: 33 },
        { name: 'Jane', age: 33 },
        { name: 'Jack', age: 17 },
        { name: 'Jill', age: 16 }
      ]
    }
  },
  computed: {
    legalUsers() {
      return this.users
        .filter(user => user.age >= 18)
        .sort((a,b) => {

          // a before o - Jane takes precedence
          if (a.name > b.name) { return  1 }
          // o before a - John takes precedence
          if (b.name > a.name) { return -1 }

          // both names are the same
          return 0
        })
    }
  }
}
</script>

If we save the file and take a look in the browser, the users are sorted in alphabetical order.

Easier sorting with lodash orderBy

The sort method can be a little confusing, especially for beginners. Luckily, lodash provides us with the orderBy method as an easier alternative.

But first, what is Lodash? Lodash is a small utility library for Javascript that provides helpful functions to make working with things like arrays very easy. It’s installed by default when we generate a project with the Vue CLI.

note If your project doesn’t include it for some reason, we cover how to install it at the end of this section.

Lodash’s orderBy method takes 2 arguments and an optional third.

  • The array to apply sorting to.
  • The property name(s) we want to order by in an array.
  • (Optional) The order as either “asc” or “desc” in an array.

The default value for the third parameter is “asc”, so if we want to sort in ascending order, we can skip it.

Example: orderBy
orderBy(
  array,
  ['property', 'property', '...'],
  ['asc'/'desc']
)

Like any other package or module, we need to import it at the top of the script block before we can use it. But instead of importing the whole package, we only want the orderBy module.

Example: import orderBy
import { orderBy } from 'lodash'

As an example, we’ll create a separate computed property that sorts the filtered users in ascending order by name . Then we’ll use the new ordered computed property in the template instead of the filtered one.

Example: src/App.vue
<template>
  <p v-for="user in orderedUsers" :key="user.name">
    {{ user.name }}
  </p>
</template>

<script>
import { orderBy } from 'lodash'

export default {
  data() {
    return {
      users: [
        { name: 'John', age: 33 },
        { name: 'Jane', age: 33 },
        { name: 'Jack', age: 17 },
        { name: 'Jill', age: 16 }
      ]
    }
  },
  computed: {
    // filter users
    legalUsers() {
      return this.users.filter(user => user.age >= 18)
    },
    // sort filtered users
    orderedUsers() {
      return sortBy(this.legalUsers, ['name'])
    }
  }
}
</script>

If we save the file and take a look in the browser, the users are again sorted in alphabetical order. But this time the code is cleaner and much easier to read.

note Even though Lodash is helpful in many ways, it’s still an extra package that adds to the size of our project.

Installing Lodash

If your Vue project doesn’t include Lodash, you can install it separately with npm. In this case, you have two options.

To install the full package, execute the following command in your project directory.

Command: full Lodash
npm install lodash --save

To install only the orderBy module, execute the command below.

Command: orderBy module
npm install lodash.orderby --save

The --save flag will install it as a project dependency.