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:
This template contains a display picture and a name. A list with this template would like the image below:
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.