Custom elements in iteration require 'v-bind:key' directives - vue.js

In my Nuxt app I have the following line that triggers the error mentioned in the title of this question:
<template v-for="(project, index) in existingProjects">
<span :key="project.projectId"></span>
I tried to have the :key attribute on the template element and I also tried to use just index as the key, to no avail.
Any idea?

There are multiple ways to solve your problem :
You want to iterate on a template :
You have to put a key on all elements in your template because you can not put a key on a template: <template> cannot be keyed. Place the key on real elements instead.
<template v-for="(project, index) in existingProjects">
<span :key="project.projectId">foo</span>
<div :key="project.projectId">bar</div>
</template>
You can iterate on something else than a template : You just put the key on the parent html tag.
<div v-for="(project, index) in existingProjects" :key="project.projectId">
<span>foo</span>
<div>bar</div>
</div>

Related

How to keep track of parent element in v-for loop?

I'm trying to change text inside div elements that contain html tags into actual html. I have a v-for loop which lists all the text items into divs.
<li v-for="item in items">
<div id="description" class="content">{{item.description}}</div>
</li>
The whole text just includes the html element rather than turning it into html which is not what I want.
I thought about pumping it through a function that would call document.innerHTML() on it but Im not sure how to make the parent have a unique ID to call it on. I'd like to keep track of the parent the item is from with either an unique ID or as some sort of parameter.
Answering this for anyone who doesn't read the comments like me :-)
v-html will render an HTML snippet to the page.
The second part of your question involves telling Vue that each element is unique. You do this with a :key. These give Vue's Virtual DOM a unique ID for your element. You can use a unique element in your data for the key or an iteration number.
If you will need to access the ID for something else OUTSIDE of Vue's reactivity, use a Vue ref.
So combining all of that your code becomes:
<li v-for="item in items" :key="item.name" ref="myItems">
<div id="description" class="content" v-html="item.description"></div>
</li>
or with a number for the key
<li v-for="(item, n) in items" :key="n" ref="myItems">
<div id="description" class="content" v-html="item.description"></div>
</li>
Note: when ref's are used in a v-for they produce an array. So in this case myItems.length == items.length and myItems[n] is a unique reference.

Elements in iteration expect to have 'v-bind:key' directives in VueApp

This error comes up:
[vue/require-v-for-key]
Elements in iteration expect to have 'v-bind:key' directives.
Codepen - https://codesandbox.io/s/competent-pine-8u9mt?file=/src/components/Laptops.vue:386-393
Does anybody know how to solve this error?
When using v-for, vue expects a unique key for each iteration. You can achieve this by binding an index key to your div element
<div v-for="(product, index) in products" :key="index">
// code to execute
</div>
:key is shorthand for v-bind:key

How to setup multiple keys for components in template tag using v-for?

I wanted to render a list using v-for. It's simple enough, the documentation explains almost every use case. I want it to look like that:
<template v-for="(item, index) in items" :key="index">
<CustomComponent :item="item"/>
<Separator v-if="index !== items.length-1"/>
</template>
Unfortunately, the documentation does not say how to set a key for multiple custom components in one v-for.
Obviously, I don't want to include separator to my custom component, because it is used in other places too. Code I have pasted is generating those errors:
'template' cannot be keyed. Place the key on real elements instead.
I can set a key on component and separator using an index but I got errors: Duplicate keys detected: 'x'. This may cause an update error.
For now, I'm doing it like that but it's an ugly hack and would not work with more components in one template.
<template v-for="(item, index) in items">
<CustomComponent :item="item" :key="(index+1)*-1"/>
<Separator v-if="index !== items.length-1" :key="(index+1)"/>
</template>
Example from documentation explains templates on the list with basic components which does not require keys.
Does anyone know how should I do it correctly?
Ps. It is not recommended to use v-if on v-for. Could someone suggest how to change my code not to use v-if but don't render separator under the last element?
Here is how I was able to generate a key -- you could customize the generateKey method to return whatever you like.
<template>
<div>
<div
v-for="(item, index) in items"
:key="generateKey(item, index)"
>Item {{ index }} : {{ item }}</div>
</div>
</template>
<script>
export default {
data() {
return {
items: ["Sun", "Moon", "Stars", "Sky"]
};
},
methods: {
generateKey(item, index) {
const uniqueKey = `${item}-${index}`;
return uniqueKey;
}
}
};
</script>
Working Example: https://codesandbox.io/s/30ojo1683p
I was talking with a friend and he suggested the simplest and in my opinion the best solution. Just add a component prefix to every key e.g:
<template v-for="(item, index) in items">
<CustomComponent :item="item" :key="'custom_component-'+index"/>
<Separator v-if="index !== items.length-1" :key="'separator-'+index"/>
</template>

Getting Error "Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key" in index.vue

I am new to vue.js. I have a simple index.vue which tries to connect to contentful and display the entries from contentful. My code in index.vue looks like this:
<template>
<div>
<!-- render data of the person -->
<h1>{{ person.fields.name }}</h1>
<!-- render blog posts -->
<ul>
<li v-for="post in posts">
{{ post.fields.title }}
</li>
</ul>
</div>
</template>
<script>
import {createClient} from '~/plugins/contentful.js'
const client = createClient()
However when I try localhost:3000 from my browser...it comes back with the following error:
ERROR in ./pages/index.vue
Module Error (from ./node_modules/eslint-loader/index.js):
C:\Users\admin\pdress\pages\index.vue
7:7 error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key
✖ 1 problem (1 error, 0 warnings)
I appreciate if someone can help me out to proceed further with my learning on vue.js please. Thanks in advance
What #ljubadr suggested is right. You need to do this:
<li v-for="post in posts" v-bind:key="post.id">
<!-- OR USE SHORTCUT NOTATION -->
<li v-for="post in posts" :key="post.id">
The reason has to do with performance. Attribute key helps Vue determine unique items in a list. Consider the example of sorting. If your UI has a sort button for posts, then your the order of items in post array will change. But does that mean Vue is going to re-render entire list? Of course not! Using :key it can determine if the item was already rendered on UI. It simply shuffles the DOM nodes and saves expensive rendering cycles.
Secondly, if you have complex components within your list when you are using v-for and :key is not provided, then whenever the list is changed or re-ordered, it simply changes the DOM but doesn't destroy existing components and that can cause local state mismatch. That is why it is must to have :key attribute.
Read Vue.js documentation for further information.
Note: Also remember that using v-for index for :key is a bad idea as it is not unique across your collection.
<template>
<div>
<!-- render data of the person -->
<h1>{{ person.fields.name }}</h1>
<!-- render blog posts -->
<ul>
<li v-for="post in posts" :key = "post">
{{ post.fields.title }}
</li>
</ul>
</div>
</template>
If this is not going to work use any unique field from your object for example if you have id in your object then,
:key = "post.id"
You must define a :key atribute for
v-for directive. And for <transition-group> tag.
for this case v-for you must explicit set,
<li v-for="(post, i) in posts" :key="i + 10">
{{ post.fields.title }}
</li>
If you noticed you can define max two argument in the v-for you must define the item (post) and can define the index of the post.

VueJs v-for how to check list undefined before continue

Refer to the template below, how to add condition to make sure the menu is not undefined inside the v-for attribute?
I've tried v-for="menu?item in menu.items:[]" and v-for="item in menu?.items" but neither works.
<div v-for="item in menu.items">{{item.text}}</div>
Put the div with the v-for directive within a <template> that checks for menu via v-if:
<template v-if="menu">
<div v-for="item in menu.items">{{ item.text }}</div>
</template>
This way, the div inside the template won't be rendered if menu does not exist.
If you really want the check within the v-for statement, like you are attempting, it would look like this:
<div v-for="item in (menu ? menu.items : [])">{{ item.text }}</div>