Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Looping with v-for

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Let's start to make our app a bit more dynamic. See these categories on the sidebar? They are 100% hardcoded in the template. Boring!

Eventually, when the page loads, we'll make an Ajax request to dynamically load the real categories from our API. This means that the categories will be empty at first and then will change to be the real categories as soon as that Ajax call finishes. And so technically, the categories are something that will change during the lifetime of our Vue app. And anything that changes must live as a key on data.

Creating categories Data

Cool! Let's add our first piece of data to sidebar: data(), then return an object with categories: set to an array. We're going to worry about the Ajax call in a few minutes. For now, let's set the initial data to some hardcoded categories. Each category can look however we want - how about an object with a name property set to our top-selling category - "Dot matrix printers" and a link property set to # for now.

Copy this and create a second category. Oh! But I can't misspell the cool "Dot Matrix" category! Shame on me! Set the second category to something just as modern: "Iomega Zip Drives". If you don't know what that is... you're definitely younger than I am.

... lines 1 - 30
export default {
... line 32
data() {
return {
categories: [
name: 'Dot Matrix Printers',
link: '#',
name: 'Iomega Zip Drives',
link: '#',
... lines 49 - 63

Using v-for

Now that our component has a data called categories, we have a variable called categories in the template! But... we can't just render it with {{ categories }}. Nope, we need to loop over the categories.

Like Twig and its {% for or {% if tags, Vue has a number of custom syntaxes, which are called directives. We'll see the most important ones in this tutorial.

And while some of the Vue directives work a lot like Twig tags, they look a bit different. The directive for looping is called v-for, but instead of being a standalone tag, we put it right on the element that we want to loop.

Check it out: I'll split the <li> onto multiple lines for readability, and then say v-for="category in categories".

... lines 2 - 14
v-for="category in categories"
... lines 19 - 24
... lines 26 - 27
... lines 29 - 63

That totally custom syntax will loop over the categories data and make a new variable called category available inside the li.

Now... life is easy! For the href="", remember: each category is an object with name and link properties. So we basically want to say category.href. I mean, category.link! I'll catch that mistake in a minute!

But... this won't work yet because it would literally print that string. To make it dynamic, use :href.

... lines 1 - 14
v-for="category in categories"
... line 23
... lines 26 - 63

Now the contents of the attribute are JavaScript, and category.link is perfectly valid JavaScript.

Below, we can print the name with {{ category.name }}.

... lines 1 - 18
... lines 20 - 21
{{ category.name }}
... lines 25 - 63

That's it! I'll remove the other hardcoded li.

Let's go check it out! When we move over, thanks to our dev-server in hot mode, we don't even need to refresh! The "All Products" is hardcoded, but the two categories below this are dynamic! Oh, but let me fix my mistake from earlier: use category.link because the data has a link property. That looks better.

The Purpose of the key Attribute

Back in my editor, ESLint is, once again, mad at me! And that's usually a good hint that I'm doing something silly! It says:

Elements in iteration expected to have v-bind:key.

That's a fancy way of saying that whenever you use v-for you're supposed to give that element a key attribute, which is any unique identifier for each item in the loop.

Obviously... our code works without it. The problem comes if we updated the categories data. Without a key attribute, Vue has a hard time figuring out which category updated... and which element to re-render.

To help Vue out, after v-for, always add a key="". Normally you'll set this to something like category.id - some unique key for each item. In this case, because we're looping over an array we invented, we can use the Array index.

To get access to the array index, change the v-for to category, index. Now say key="index". But... once again... this isn't quite what we want: this would set the key attribute to the string index. To make it dynamic, change it to :key="index".

... lines 1 - 14
v-for="(category, index) in categories"
... line 18
... lines 20 - 25
... lines 27 - 64

Hey! Now we even get autocomplete!

Back on the browser, we won't see any difference, but if we updated the categories, Vue would re-render perfectly.

The Directives API Docs

We've now seen two Vue directives. Google for "Vue API" to find a page called Vue.component - API. I love this page! It... well... talks about pretty much everything that exists in Vue.

On the left, I'm going to scroll past tons of things to a section called "Directives". Notice that there aren't that many directives in Vue - about 15 or so. And we've already seen both v-bind and v-for. We'll talk about the other important directives later. For each one, you can get info about how it works.

The custom key Attribute

Oh, and there's one tiny detail I want to mention before we keep going. Back on the site, inspect element on the li. It has class="nav-item", but it does not have a key attribute?

There's some magic going on. Normally, if you add foo="bar" to an element, that element - of course - will have a foo attribute! We know that the true purpose of the key attribute was to help Vue internally... but shouldn't that also cause our element to have a key attribute?

Back on the docs, right below the Directives section is another one called "Special Attributes" and you see that key is one of them. Basically, if you add an attribute in Vue, it will render as an attribute... unless it's one of these special attributes. It's not really an important detail, but if you were wondering why everything other than key is rendered as an attribute, this is your answer. Vue is hiding it, because it knows it's internal.

Next: Vue is more than data and re-rendering: it's also about responding to user interaction. Let's add our first behavior: a link that will collapse and expand the sidebar.

Leave a comment!

Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
    "devDependencies": {
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.0
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.1.0
        "eslint-plugin-import": "^2.19.1", // 2.20.2
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.5
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.11
        "vue-loader": "^15.9.1", // 15.9.2
        "vue-template-compiler": "^2.6.11", // 2.6.11
        "webpack-notifier": "^1.6.0" // 1.8.0