Rendering Lists in Vue

Rendering Lists in Vue


Web applications handle lists of data for various reasons: showing a list of registered users, checkout page with items to be purchased or a log of previous transactions.

A list is such a common construct that it’s a good fit for an abstraction. Vue makes it convenient to render lists of data with a nice syntax. In this article, we’ll look at lists and how to handle them in Vue.

An Overview of Lists

A list is most often represented as an array. For example:

["James", "Jane", "Kane", "Deborah"]

This is an array of people’s names. Another list can also look like:

[
  {
    name: "James",
    photoURL: "https://path-to-image.jpg"
  },
  {
    name: "Deborah",
    photoURL: "https://path-to-image.jpg"
  }
]

In this case, we have a list, where each item is an object containing a name and a photoURL. It can be a list of users and the URL of their display picture. This is the first ingredient required to render lists in Vue: a data source.

The second item required is a template. A template is just a piece of the UI that will be displayed for each item in the list. Below is an example of a template:

Rendering Lists in vue

This template contains a display picture and a name. A list with this template would like the image below:

Rendering Lists in vue

The only thing that changes for each list item in the display picture and name but we already know that each list item would contain both. The markup for this would look similar to this:

<ul>
  <li>
    <img src="path-to-image" alt="picture text" />
    <p>T Ritika Singh</p>
  </li>
  <li>
    <img src="path-to-image" alt="picture text" />
    <p>James</p>
  </li>
  <li>
    <img src="path-to-image" alt="picture alt text" />
    <p>Anderson</p>
  </li>
</ul>

Now, imagine we had a list of 100 users that we wanted to display. It would take a while to write the markup that would render it. This is where we can take advantage of Vue’s v-for directive to achieve this in an efficient manner.

Lists in Vue

Let’s look at the markup for the list we defined in the previous section:

<ul>
  <li>
    <img src="path-to-image" alt="picture text" />
    <p>T Ritika Singh</p>
  </li>
  <li>
    <img src="path-to-image" alt="picture text" />
    <p>James</p>
  </li>
  <li>
    <img src="path-to-image" alt="picture text" />
    <p>Anderson</p>
  </li>
</ul>

If you look closely, you will see a pattern emerge for each item. We could easily extract the pattern which leaves us with:

<li>
  <img src="path-to-image" alt="picture text" />
  <p>Name</p>
</li>

This is the template we need for the list. Let’s look at a simple Vue component to see how we can render a list from a data source and a template.

// Example.vue
<template>
  <ul>
    <li>
      <img src="path-to-image" alt="picture text" />
      <p>T Ritika Singh</p>
    </li>
    <li>
      <img src="path-to-image" alt="picture text" />
      <p>James</p>
    </li>
    <li>
      <img src="path-to-image" alt="picture text" />
      <p>Anderson</p>
    </li>
  </ul>
</template>

<script>
export default {};
</script>

Here, we have just added our markup to the Vue component. As mentioned earlier, we need a data source to render a list so let’s create one.

We can add our list of users as a data property:

export default {
  data() {
    return {
      users: [
        {
          name: "T Ritika Singh",
          photoURL: "https://path-to-image.jpg"
        },
        {
          name: "James",
          photoURL: "https://path-to-image.jpg"
        },
        {
          name: "Anderson",
          photoURL: "https://path-to-image.jpg"
        }
      ],
    }
  },
};

Now we have our data source, let’s use it. We can modify the markup now like so:

<ul>
  <li v-for="user in users" v-bind:key="user.name">
    <img v-bind:src="user.photoURL" alt="picture text" />
    <p>{{ user.name }}</p>
  </li>
</ul>

v-for takes the data source users which we created previously and renders the template for each item in the array. user is the item being iterated on and is updated each time round the loop. Vue recommends adding a key to each item to keep track of each node’s identity which is used to reuse and reorder elements. key should be unique else you would get a warning in the developer tools console and you don’t get this benefits. Lastly, a key should be a primitive type (think strings and numbers).

In the template, we bind the img src attribute to the photoURL. And use the mustache syntax {{ }} to bind the text name. Now we can have as many items in our data source as we want without having to repeat the same markup over and over.

Nested Lists

We can also have nested lists in Vue. Take this array for example:

[
  {
    name: "T Ritika Singh",
    photoURL: "https://path-to-image.jpg",
    addresses: [
      "Address 1",
      "Address 2"
    ]
  },
  {
    name: "James",
    photoURL: "https://path-to-image.jpg",
    addresses: [
      "Address 1",
      "Address 2"
    ]
  },
  {
    name: "Anderson",
    photoURL: "https://path-to-image.jpg",
    addresses: [
      "Address 1",
      "Address 2"
    ]
  },
]

Here, addresses is an array of a user’s addresses with the root item already being an array. We can modify our template like so:

<ul>
  <li v-for="user in users" v-bind:key="user.name">
    <img v-bind:src="user.photoURL" alt="picture text" />
    <p>{{ user.name }}</p>
    <p v-for="address in user.addresses">{{ address }}</p>
  </li>
</ul>

Here, we have a v-for inside a v-for. user.addresses is an array so we just add another v-for to loop through it as well. The template here is just a <p> for each address.

Working with Lists from External Data Sources

In practice, most times you will be fetching the data that’s to be rendered from an API. You can check out this demo app that works with external APIs. The API returns a list of dog breeds which are rendered on the page.

To start off, you create a data property for the array:

data() {
  return {
    dogs: [],
  };
}

It starts off as an empty array. Since the array is empty, v-for does not render anything. Then you can make your API call in the mounted lifecycle hook and update dogs when a response is gotten.

async mounted() {
  try {
    const response = await fetch('https://dog.ceo/api/breeds/list/all');
    const { message } = await response.json();
    this.dogs = Object.keys(message);
  } catch (error) {
    console.log(error);
  }
}

Using fetch, we make a request to the endpoint. Once we get a response, we update the dogs array. Because v-for listens to changes to the array, the UI is automatically updated whenever there are changes to the array.

NB: v-for only observes mutations on the array itself, not sub-properties in the array i.e. User[2].name = “John” would not trigger an update.

Conclusion

In this article, we’ve seen how we can display lists effectively in Vue. We also looked at rendering nested lists and also working with lists from external sources. If you are interested in the source code used in the demo, it’s available on GitHub.


Share on social media

//