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

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>

Related

creating a loop to loop over the componennt created

I have this data which is returning me the labels and every I need to create a component.
The component is already built when I pass the values. Now I want to create it as a for loop so that I can keep on adding entries, and it will create components as needed.
This is what I've tried:
data() {
return {
options: [{
heading: 'Welcome to the Card',
subheading: 'Manage your Profile here'
},
{
heading: 'Pay your bills',
subheading: 'Manage your bills and payments here'
}
]
}
}
I am trying to loop it over like this
<div v-for="(value, key) in options">
<componentName {{key}} = {{value}}/>
</div>
Previously, the above code was like this:
<componentName :heading='Welcome to the Card' :subheading='Manage your Profile here'/>
Which works well but to add more I have to recreate this <componentName which I want to avoid. I want to keep one entry and feed it with array of objects
I'm using Vue2. What am I doing wrong here?
You're very close. Given your data, the template would need to look like:
<div v-for="(option, index) in options" :key="index">
<h3>{{ option.heading }}</h3>
<p>{{ option.subheading}}</p>
</div>
Or, if you've got a custom component, which takes heading and subheading as props:
<componentName
v-for="(option, index) in options"
:key="index"
:heading="option.heading"
:subheading="option.subheading"
/>
If you can share a bit more of your code, I can create a runnable snippet, if that would be helpful.

How do set a default value in select option in element UI for Vue?

I am working on a Vue app made with element UI and I have a bunch of el-select whose el-options need to have default values, meaning the form select fields already have one option pre-selected (of course the user can still choose other options).
I cannot find any attribute in the official doc https://element.eleme.io/#/en-US/component/select#select
But there should be a way to achieve this right?
This is my code
<el-form-item label="some label" prop="someprop">
<el-select v-model="form.status" filterable clearable>
<el-option
v-for="(item, index) in config.status"
:key="index"
:label="item"
:value="index"
how to have default option here??
>
</el-option>
</el-select>
</el-form-item>
Just put in the form.status the indexes that should be pre-selected. Vue will take care of the rest.
data(){
return {
form: { status: ['thisWillBePreSelected'], },
}
}

How to append a static components to a dynamic ones in Vue?

What I'm trying to achieve is this: I have a <v-window> parent component that takes <v-window-item> children. The first child loops thru a Vuex getter that returns an object and depending on its content dynamically visualizes cards. However, I have another static component that is like a summary and contains a logout button that I want to append to the last dynamic <v-window> generated from the store. Here's how I've set up my code so far:
<v-window v-model="reportPage">
<v-window-item v-for="card in getSelectedCard" :key="card.id">
</v-window-item>
</v-window>
Can someone give me some pointers on how to achieve that? Thanks in advance!
I think there are a few ways to achieve such a thing, the one I would use is conditional rendering based on the current index:
new Vue({
el: "#app",
data: {
someList: [ "first", "middle", "last" ]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ol>
<li v-for="(item, index) in someList">
{{ item }}
<span v-if="index === someList.length - 1">
- logout button component here
</span>
</li>
</ol>
</div>
Of course the content of my v-if could be a prop of your v-window-item:
<v-window-item v-for="(card, index) in getSelectedCard" :key="card.id" show-logout-button="index === getSelectedCard.length - 1">
Or if you have a slot in your v-window-item:
<v-window-item v-for="(card, index) in getSelectedCard" :key="card.id">
<logout-button v-if="index === getSelectedCard.length - 1" />
</v-window-item>
There are slots that can help you. You simply need to add a <slot></slot> to your child component, then you'll be able to put whatever you like inside your child tag !

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

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>

[Vue warn]: Duplicate keys detected: x. This may cause an update error

I keep getting an error when I add an item to the array which has duplicate id.
i.e.
active_widgets:Array[4]
0:Object
id:1
name:"Text Blocks"
selected:false
set:false
1:Object
id:3
name:"Bibliographies/References"
selected:false
set:false
2:Object
id:1
name:"Text Blocks"
selected:false
set:false
3:Object
id:2
name:"Free Text"
selected:"Test"
set:false
In my scenario, 'id' element may be duplicate because the user can have the same widget on the page multiple times. I want to know if I can suppress or remove the warning that VueJS keeps throwing in the console.
Same key for different v-for loops causing this warning. You can avoid this using different key for different v-for loops.
<div v-for="(item, i) in items" :key="i"></div>
<div v-for="(item, i) in items2" :key="'A'+ i"></div>
<div v-for="(item, i) in items3" :key="'B' + i"></div>
Here, A and B are just sample characters. You can basically use any character instead of those (just for uniqueness).
An alternative method:
Nesting the v-for elements inside any other element also seems to work.
<div>
<div v-for="(item, index) in items" :key="index"></div>
</div>
<div>
<div v-for="(item, index) in items2" :key="index"></div>
</div>
You need to bind to the key with a unique value. Not doing so will cause problems in your application when a change in data for a component with one key updates that component and the other component with the duplicate key.
You should assign a unique key property to each of the items in the active_widgets array and then bind the key to that property.
Without seeing any of your code, I don't know what your unique use case is. But here are a couple ways you could add a unique key property to the items in your array.
Here's an example doing that in the created method.
created() {
this.active_widgets.forEach((item, index) => this.$set(item, 'key', index));
}
If you need to add a unique key when an item is added to this array, you could maintain a counter and increment it each time an addition is made:
let WidgetCount = 0;
export default {
data() {
return { active_widgets: [] }
},
methods: {
addWidget(id, name) {
this.active_widgets.push({
id,
name,
selected: false,
set: false,
key: WidgetCount++
})
}
}
}
use different key name your problem will be solved.
<div v-for="(item, i) in items" :key="i"></div>
<div v-for="(item, j) in items2" :key="j" :data-index="j"></div>
or
<div v-for="(item, i) in items2" :key="'i+item.id" :data-index="i"></div>
<template v-for="it in items">
<li :key="it.id + '-name'">{{it.name}}</li>
</template>
https://github.com/vuejs/vue/issues/7323
<div v-for="(data, index)" in active_widgets" :key="index">
{{data.id}}
{{data.name}}
{{data.selected}}
{{data.set}}
</div>
I solved this by creating a unique key function to add keys to each of my arrays. Then using it in v-for as the key...
<div
class="postBlob"
v-for="{
key,
user,
post,
dateTime
} in localPostData.slice().reverse()"
:key="key"
>
<strong class="userBlobIndy">{{ user }} </strong>
<h2 class="postBlobIndy">
{{ post }}
<p>{{ dateTime }}</p>
</h2>
</div>
you can use this example
<template>
<div >
<div v-for="categori in blogs" id="blog-body" :key="categori.title" >
<h2 >{{categori.title}}</h2>
<h3>{{categori.contact }}</h3>
</div>
</div>
</template>
<script>
export default {
data(){
return{
blogs:[
{title:'this is title 1',contact : ' this is contact for test javascript'},
{title:'this new title ',contact : ' this is contact for vue'},
{title:'this is new title 2',contact : ' this is contact for vue js'}
]
}
},
}
</script>