loading

Vue v-for Components

Using v-for, components can be combined to create a large number of identical pieces.

Additionally useful when producing elements with v-for from a component is the ability to dynamically assign props based on values from an array.

Create Component Elements with v-for

Now, using v-for, we will generate component elements based on an array containing the names of food items.

Example

App.vue:

				
					<template>
  <h1>Food</h1>
  <p>Components created with v-for based on an array.</p>
  <div id="wrapper">
    <food-item
      v-for="x in foods"
      v-bind:food-name="x"/>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        foods: ['Apples','Pizza','Rice','Fish','Cake']
      };
    }
  }
</script>
				
			

FoodItem.vue:

				
					<template>
  <div>
    <h2>{{ foodName }}</h2>
  </div>
</template>

<script>
  export default {
    props: ['foodName']
  }
</script>
				
			

v-bind Shorthand

We utilize v-bind to dynamically bind props, and since we will be using v-bind a lot more now than in the past, we will use the v-bind: shorthand : for the remainder of this lesson.

The 'key' Attribute

Because of the way Vue updates elements produced using v-for, issues may arise if we edit the array after the elements have been formed. Since Vue reuses elements to maximize efficiency, removing an item may result in the reuse of previously created elements rather than the creation of all new ones, potentially changing element characteristics.

Since elements lack a unique identifier, they are wrongly reused. To prevent this, we utilize the key attribute, which allows Vue to distinguish between the elements.

First, let’s create a web page containing foods using v-for to display the food name, description, favorite food image, and button to alter the favorite status. Then, we will develop erroneous behavior without the key property.

App.vue:

				
					<template>
  <h1>Food</h1>
  <p>Food items are generated with v-for from the 'foods' array.</p>
  <div id="wrapper">
    <food-item
      v-for="x in foods"
      :food-name="x.name"
      :food-desc="x.desc"
      :is-favorite="x.favorite"/>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        foods: [
          { name: 'Apples',
            desc: 'Apples are a type of fruit that grow on trees.',
            favorite: true },
          { name: 'Pizza',
            desc: 'Pizza has a bread base with tomato sauce, cheese, and toppings on top.',
            favorite: false },
          { name: 'Rice',
            desc: 'Rice is a type of grain that people like to eat.',
            favorite: false }
          { name: 'Fish',
            desc: 'Fish is an animal that lives in water.',
            favorite: true }
          { name: 'Cake',
            desc: 'Cake is something sweet that tastes good.',
            favorite: false }
        ]
      };
    }
  }
</script>

<style>
  #wrapper {
    display: flex;
    flex-wrap: wrap;
  }
  #wrapper > div {
    border: dashed black 1px;
    flex-basis: 120px;
    margin: 10px;
    padding: 10px;
    background-color: lightgreen;
  }
</style>
				
			

FoodItem.vue:

				
					<template>
  <div>
    <h2>
      {{ foodName }}
      <img decoding="async" src="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%200%200'%3E%3C/svg%3E" v-show="foodIsFavorite" alt="Vue V-For Components -" title="Vue V-For Components 1" data-lazy-src="/img_quality.svg"><noscript><img decoding="async" src="/img_quality.svg" v-show="foodIsFavorite" alt="Vue V-For Components -" title="Vue V-For Components 1"></noscript>
    </h2>
    <p>{{ foodDesc }}</p>
    <button v-on:click="toggleFavorite">Favorite</button>
  </div>
</template>

<script>
  export default {
    props: ['foodName','foodDesc','isFavorite'],
    data() {
      return {
        foodIsFavorite: this.isFavorite
      }
    },
    methods: {
      toggleFavorite() {
        this.foodIsFavorite = !this.foodIsFavorite;
      }
    }
  }
</script>

<style>
  img {
    height: 1.5em;
    float: right;
  }
</style>
				
			

Let’s make a button that eliminates the second entry in the array to demonstrate the requirement for the key attribute. This is incorrect because the favorite picture is moved from the “Fish” element to the “Cake” element without the key attribute:

Example

The addition of a button is the sole modification from the preceding example:

				
					<button @click="removeItem">Remove Item</button>
				
			

and a method:

				
					methods: {
  removeItem() {
    this.foods.splice(1,1);
  }
}
				
			

in App.vue.

As previously noted, Vue optimizes the page by reusing elements, but at the same time, Vue is unable to properly distinguish between the elements. This is why the favorite picture switches from “fish” to “cake” when an element is removed. For this reason, whenever we generate items using v-for, we should always add the key attribute to uniquely mark each element. This issue is resolved when the key attribute is used.

Since the array element index varies when array elements are added and removed, we do not utilize it as the key attribute value. The food products already have unique names, so we can use that instead of creating a new data field to maintain a unique value for each item, like an ID number:

Example

To solve the issue and uniquely identify every element made with v-for, we only need to add one line to App.vue:

				
					<food-item
  v-for="x in foods"
  :key="x.name"
  :food-name="x.name"
  :food-desc="x.desc"
  :is-favorite="x.favorite"
/>
				
			
Share this Doc

Vue v-for Components

Or copy link

Explore Topic