List Rendering

v-for

We can use the v-for directive to render a list of items based on an Array. The v-for directive requires a special syntax in the form of item in items, where items is the source data Array and item is an alias for the Array element being iterated on:

Example:

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

Result:

Inside v-for blocks we have full access to parent scope properties, plus a special variable $index which, as you probably have guessed, is the Array index for the current item:

<ul id="example-2">
  <li v-for="item in items">
    {{ parentMessage }} - {{ $index }} - {{ item.message }}
  </li>
</ul>
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})

Result:

Alternatively, you can also specify an alias for the index (or the key if v-for is used on an Object):

<div v-for="(index, item) in items">
  {{ index }} {{ item.message }}
</div>

Starting in 1.0.17 you can also use of as the delimiter instead of in, so that it is closer to JavaScript syntax for iterators:

<div v-for="item of items"></div>

Template v-for

Similar to template v-if, you can also use a <template> tag with v-for to render a block of multiple elements. For example:

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>

Array Change Detection

Mutation Methods

Vue.js wraps an observed Array’s mutation methods so they will also trigger View updates. The wrapped methods are:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

You can open the console and play with the previous examples’ items array by calling its mutation methods. For example: example1.items.push({ message: 'Baz' }).

Replacing an Array

Mutation methods, as the name suggests, mutate the original Array they are called on. In comparison, there are also non-mutating methods, e.g. filter(), concat() and slice(), which do not mutate the original Array but always return a new Array. When working with non-mutating methods, you can just replace the old Array with the new one:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})

You might think this will cause Vue.js to throw away the existing DOM and re-render the entire list - luckily that is not the case. Vue.js implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping objects is a very efficient operation.

track-by

In some cases, you might need to replace the Array with completely new objects - e.g. ones created from an API call. Since by default v-for determines the reusability of existing scopes and DOM elements by tracking the identity of its data object, this could cause the entire list to be re-rendered. However, if each of your data objects has a unique id property, then you can use a track-by special attribute to give Vue.js a hint so that it can reuse existing instances as much as possible.

For example, if your data looks like this:

{
  items: [
    { _uid: '88f869d', ... },
    { _uid: '7496c10', ... }
  ]
}

Then you can give the hint like this:

<div v-for="item in items" track-by="_uid">
  <!-- content -->
</div>

Later on, when you replace the items array and Vue.js encounters a new object with _uid: '88f869d', it knows it can reuse the existing scope and DOM elements associated with the same _uid.

track-by $index

If you don’t have a unique key to track by, you can also use track-by="$index", which will force v-for into in-place update mode: fragments are no longer moved around, they simply get flushed with the new value at the corresponding index. This mode can also handle duplicate values in the source array.

This can make Array replacement extremely efficient, but it comes at a trade-off. Because DOM nodes are no longer moved to reflect the change in order, temporary state like DOM input values and component private state can become out of sync. So, be careful when using track-by="$index" if the v-for block contains form input elements or child components.

Caveats

Due to limitations of JavaScript, Vue.js cannot detect the following changes to an Array:

  1. When you directly set an item with the index, e.g. vm.items[0] = {};
  2. When you modify the length of the Array, e.g. vm.items.length = 0.

To deal with caveat (1), Vue.js augments observed Arrays with a $set() method:

// same as `example1.items[0] = ...` but triggers view update
example1.items.$set(0, { childMsg: 'Changed!'})

To deal with caveat (2), just replace items with an empty array instead.

In addition to $set(), Vue.js also augments Arrays with a convenience method $remove(), which searches for and removes an item from target Array by calling splice() internally. So instead of:

var index = this.items.indexOf(item)
if (index !== -1) {
  this.items.splice(index, 1)
}

You can just do:

this.items.$remove(item)

Using Object.freeze()

When iterating over an array of objects frozen with Object.freeze(), you need to explicitly use a track-by key. A warning will be displayed in this scenario when Vue.js is unable to track objects automatically.

Object v-for

You can also use v-for to iterate through the properties of an Object. In addition to $index, each scope will have access to another special property $key.

<ul id="repeat-object" class="demo">
  <li v-for="value in object">
    {{ $key }} : {{ value }}
  </li>
</ul>
new Vue({
  el: '#repeat-object',
  data: {
    object: {
      FirstName: 'John',
      LastName: 'Doe',
      Age: 30
    }
  }
})

Result:

You can also provide an alias for the key:

<div v-for="(key, val) in object">
  {{ key }} {{ val }}
</div>

When iterating over an Object, the order is based on the key enumeration order of Object.keys(), which is not guaranteed to be consistent in all JavaScript engine implementations.

Range v-for

v-for can also take an integer Number. In this case it will repeat the template that many times.

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>

Result:

Displaying Filtered/Sorted Results

Sometimes we only need to display a filtered or sorted version of the Array without actually mutating or resetting the original data. There are two options to achieve this:

  1. Create a computed property that returns the filtered or sorted Array;
  2. Use the built-in filterBy and orderBy filters.

A computed property would give you finer-grained control and more flexibility since it’s full JavaScript; but the filters can be more convenient for common use cases. For detailed usage of the Array filters, check out their documentation.

© 2013–present Yuxi Evan You
Licensed under the MIT License.
https://v1.vuejs.org/guide/list.html