How to add number or id each element in vue js? - vue.js

in my case i create dynamic form (like todo app) in another components. i want accesibility for other actions. therefore i must set id or number each created element(div). when i add increment method console shows duplicate key error.
how can i add number or id each element?
For examples: this is 1. created element,
this is 2. created element,
this is 3. created element,
some codes:
methods: {
...mapActions(["setAdList"]),
ilanVer() {
this.alert = true;
let adListObj = {
adName: this.adName,
text: this.text,
province: this.province,
education: this.education,
solder: this.solder,
id: this.id
}
this.setAdList(adListObj);
this.id += 1;
console.log(this.id)
},
}
<template>
<!-- eslint-disable max-len -->
<div class="ad-box">
<h1 class="d-flex justify-content-center align-items-center mt-2">İlanlarım</h1>
<hr />
<div class="ilan-list">
<b-card :title=item.adName v-for="(item) in adList" :key="item.id" class="border border-info rounded w-50">
<b-badge class="ml-3">{{item.id}}</b-badge>
<p class="card-text">Aday Özellikleri: <span class="text-center">{{item.text}}</span> </p>
<p class="card-text">Şehir: {{item.province}}</p>
<p class="card-text">Eğitim: {{item.education}}</p>
<p class="card-text">Askerlik: {{item.solder}}</p>
<router-link to="/ilandetayi/:id">
<b-button class="btn-outline-success mr-2">İlanlarıma Git</b-button>
</router-link>
</b-card>
<hr />
</div>
</div>
</template>

You can use index when using v-for directive
In your case
v-for="(item, index) in adList" and then inside the block index will have values 0, 1, 2, ....

You can use Index item of v-for to define the unique id
v-for="(item, index) in adList" :key="index"
<template> <!-- eslint-disable max-len --> <div class="ad-box">
<h1 class="d-flex justify-content-center align-items-center mt-2">İlanlarım</h1>
<hr />
<div class="ilan-list">
<b-card :title=item.adName v-for="(item, index) in adList" :key="index" class="border border-info rounded w-50">
<b-badge class="ml-3">{{item.id}}</b-badge>
<p class="card-text">Aday Özellikleri: <span class="text-center">{{item.text}}</span> </p>
<p class="card-text">Şehir: {{item.province}}</p>
<p class="card-text">Eğitim: {{item.education}}</p>
<p class="card-text">Askerlik: {{item.solder}}</p>
<router-link to="/ilandetayi/:id">
<b-button class="btn-outline-success mr-2">İlanlarıma Git</b-button>
</router-link>
</b-card>
<hr />
</div> </div> </template
> Blockquote

Related

Vue plugin working on nuxt server load but not on client navigation (vue-scrollactive)

I have a Nuxt application where I've installed vue-scrollactive as a plugin.
I am using it on a child page (ie website.com/company). If I do a fresh load of website.com/company the plugin works fine - however, if I load the site from another page (ie website.com or website.com/about) using nuxt-link then the scrollactive plugin does not work.
UPDATE:
It appears to be something to do with the fetchState and the main tag appearing on v-else. I tested with dummy data using fetch and it worked.
plugins/scrollActive.js
import Vue from 'vue'
import scrollactive from 'vue-scrollactive'
Vue.use(scrollactive)
nuxt.config.js
plugins: [{ src: '~/plugins/scrollActive' }],
pages/company.vue
<div class="flex flex-col h-screen bg-gray-100">
<TheAppNavbar :company="company" />
<!-- Subnav -->
<scrollactive
:offset="30"
:highlight-first-item="true"
scroll-container-selector="#data-area"
class="block h-16 overflow-x-auto border-b dark:border-gray-600 md:hidden bg-gray-50 dark:bg-gray-800 scrollbars-hidden my-nav"
>
<div class="inline-flex items-center h-full text-lg">
<a
v-for="category in categories"
:key="category"
:href="`#${category}`"
class="flex items-center h-full text-gray-500 capitalize scrollactive-item dark:text-gray-400'"
>
<span v-if="publicCat(category)" class="mx-4 sm:mx-8">{{
category
}}</span>
</a>
</div>
</scrollactive>
<!-- Bottom section -->
<div class="flex flex-1 min-h-0">
<!-- Narrow sidebar-->
<scrollactive
:offset="30"
:highlight-first-item="true"
scroll-container-selector="#data-area"
aria-label="Sidebar"
class="hidden bg-gray-800 dark:bg-white w-28 md:flex md:flex-col md:overflow-y-auto scrollbars-hidden my-nav"
>
<div class="relative flex flex-col px-2 mt-4 space-y-2">
<a
v-if="publicCat('sports')"
href="#sports"
class="flex scrollactive-item"
>
<span class="mt-2">Sports</span>
</a>
<a
v-if="publicCat('finance')"
href="#finance"
class="flex scrollactive-item"
>
<span class="mt-2">Finance</span>
</a>
</div>
</scrollactive>
<!-- Main area -->
<div v-if="$fetchState.pending">
Loading
</div>
<div v-else-if="$fetchState.error">
An error occurred :(
</div>
<main
v-else
id="data-area"
>
<section
v-for="cat in categories"
:id="cat"
:key="cat"
aria-labelledby="primary-heading"
class="flex flex-col flex-1"
>
<div v-if="company[cat].public === true">
<div id="section-header">
<h2>
{{ cat }}
</h2>
<p v-if="formattedDate(cat) !== null">
Updated:
{{ formattedDate(cat) }}
</p>
</div>
<CompanySports
v-if="cat === 'sports'"
class="pb-20 sm:pt-8"
:financials="company.sports"
/>
<CompanyFinance
v-if="cat === 'finance'"
class="pb-20 sm:pt-8"
:financials="company.finance"
/>
</div>
</section>
<a
:href="`https://${company.website}`"
target="_blank"
>Visit Website
</a>
</main>
</div>
</div>
<script>
...
methods: {
publicCat(cat) {
if (this.company[cat]) {
if (this.company[cat].public === true) {
return true
} else {
return false
}
}
},
formattedDate(cat) {
if (this.company[cat].updated) {
const date = new Date(this.company[cat].updated.seconds * 1000)
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
})
}
return null
},
},
</script>
It was a bit hacky but I changed the main area without the v-else and added
v-if="$fetchState.pending === false" and :class="company === {} ? 'hidden' : 'block'" to a div that enclosed the area with data. Works for now but not ideal.
<!-- Main area -->
<div v-if="$fetchState.pending">
Loading
</div>
<div v-else-if="$fetchState.error">
An error occurred :(
</div>
<main
id="data-area"
>
<section
v-for="cat in categories"
:id="cat"
:key="cat"
aria-labelledby="primary-heading"
class="flex flex-col flex-1"
>
<div
v-if="$fetchState.pending === false && company[cat].public === true"
:class="company === {} ? 'hidden' : 'block'"
>
<div id="section-header">
<h2>
{{ cat }}
</h2>
<p v-if="formattedDate(cat) !== null">
Updated:
{{ formattedDate(cat) }}
</p>
</div>
<CompanySports
v-if="cat === 'sports'"
class="pb-20 sm:pt-8"
:financials="company.sports"
/>
<CompanyFinance
v-if="cat === 'finance'"
class="pb-20 sm:pt-8"
:financials="company.finance"
/>
</div>
</section>
<a
v-if="$fetchState.pending === false"
:class="company === {} ? 'hidden' : 'block'"
:href="`https://${company.website}`"
target="_blank"
>Visit Website
</a>
</main>

Is it possible to get outer loop's key inside a nested loop? VueJs

I am trying to get the outer loop's key inside the inner loops. Trying to get the radio button value with selected[item.key] but it doesn't work. It is possible to get the key of outer loop? or is that any better way to get the radio button value? The problem I encounter is that i was trying to get outer loop key as index of selected to store answer
<div v-for="(item,key) in items" :key="key">
<div> {{ item.title }}</div>
<div class="selections">
<div v-for="(selection,key) in item.selection" :key="key">
<input type="radio"
v-model="selected[item.key]
:name=item.name"
:value="selection.name">
<label :for="selection.name">{{selection.name}}</label
</div>
</div>
</div>
selected is an array ready to store those data.
item:[
{name:"item1",id:"1",selection:[
{id:1,name:"selection1"},
{id:2,name:"selection2"},
{id:3,name:"selection3"},
]
},
{name:"item2",id:"2",selection:[
{id:1,name:"selection1"},
{id:2,name:"selection2"},
{id:3,name:"selection3"},
]
},
{name:"item3",id:"3",selection:[
{id:1,name:"selection1"},
{id:2,name:"selection2"},
{id:3,name:"selection3"},
]
},
]
Try this:
<div v-for="item in items" :key="item.id">
<div> {{ item.title }}</div>
<div class="selections">
<div v-for="selection in item.selection" :key="selection.id">
<input type="radio"
v-model="selected.find(({ id }) => id === item.id).name"
:name="item.name"
:value="selection.name">
<label :for="selection.name">{{selection.name}}</label>
</div>
</div>
</div>
Or if you want to select the selection:
<div v-for="item in items" :key="item.id">
<div> {{ item.title }}</div>
<div class="selections">
<div v-for="selection in item.selection" :key="selection.id">
<input type="radio"
v-model="item.selection.find(({ id }) => id === selection.id).name"
:name="item.name"
:value="selection.name">
<label :for="selection.name">{{selection.name}}</label>
</div>
</div>
</div>

how to display data from props in b-table template tag?

i am new in vue.js and get some problem while displaying data in b-table. my data from database is fetched properly. and i passed from one file to products.vue and received data in products.vue as props. when i console my data is showing in console properly, but when i used to display data in b-table, i got some problem. data is not displaying correctly.
please let me know where is mistake in my code?
Products.vue (script tag)
<script>
export default {
props: ['category','singular', 'plural','products'],
data() {
return {
title: `All ${this.plural}`,
items: [],
editing: false,
weight_assignment: false,
model: null,
formTitle: '',
fields: [
{
key: 'index',
label: 'S.No.'
},
{
key: 'name',
sortable: true
},
{
key: 'weights'
},
{
key: 'default_weight',
sortable: true
},
{
key: 'status',
sortable: true
},
{
key: 'action'
}
],
}
</script>
Product.vue (template tag)
<template>
<div class="row">
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">{{title}}</h3>
<div class="card-tools">
<b-button variant="primary" #click="newItem">New {{singular}}</b-button>
<div></div>
</div>
</div>
<div class="card-body table-responsive p-0">
<spinner v-if="$root.loading"></spinner>
<b-table ref="table" v-for="type in this.products" :items="type" :key="type.id" :fields="fields" bordered hover :head-variant="'light'" responsive="sm" v-else>
<template v-slot:cell(index)="type">
{{ type.index + 1 }}
</template>
<template v-slot:cell(name)="type">
<b-img thumbnail fluid :src="getImageUrl(type.image)" alt="Image 1" class="thumb-img"></b-img>
{{type.name}}
</template>
<template v-slot:cell(weights)="type">
<span v-weights="type.item"></span>
</template>
<template v-slot:cell(default_weight)="type">
<span v-floatFormatter="type.default_weight"></span>{{type.unit.sname}}
</template>
<template v-slot:cell(status)="type">
<span v-if="type.status" class="text-success text-bold">Active</span>
<span class="text-danger" v-else>Inactive</span>
</template>
<template v-slot:cell(action)="data">
<a #click.prevent="editItem(data.index)"><i class="fa fa-edit" aria-hidden="true" title="Update product" v-b-tooltip.hover></i></a>
<a #click.prevent="assignWeights(data.index)"><i class="fa fa-balance-scale" title="Assign weights" aria-hidden="true" v-b-tooltip.hover></i></a>
</template>
</b-table>
</div>
</div>
</div>
</div>
</template>
v-for="type in this.products" - try to delete this key. In template tag do not use this keyword to access data or any other values.
I have spotted you pass data completely wrong way. Please use below variant and tell me if it work.
EDITED template:
<template>
<div class="row">
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">{{title}}</h3>
<div class="card-tools">
<b-button variant="primary" #click="newItem">New {{singular}}</b-button>
<div></div>
</div>
</div>
<div class="card-body table-responsive p-0">
<spinner v-if="$root.loading"></spinner>
<b-table ref="table" :items="products" :fields="fields" bordered hover :head-variant="'light'" responsive="sm" v-else>
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<template v-slot:cell(name)="data">
<b-img thumbnail fluid :src="getImageUrl(data.image)" alt="Image 1" class="thumb-img"></b-img>
{{data.name}}
</template>
<template v-slot:cell(weights)="data">
<span v-weights="data.item"></span>
</template>
<template v-slot:cell(default_weight)="data">
<span v-floatFormatter="data.default_weight"></span>{{data.unit.sname}}
</template>
<template v-slot:cell(status)="data">
<span v-if="data.status" class="text-success text-bold">Active</span>
<span class="text-danger" v-else>Inactive</span>
</template>
<template v-slot:cell(action)="data">
<a #click.prevent="editItem(data.index)"><i class="fa fa-edit" aria-hidden="true" title="Update product" v-b-tooltip.hover></i></a>
<a #click.prevent="assignWeights(data.index)"><i class="fa fa-balance-scale" title="Assign weights" aria-hidden="true" v-b-tooltip.hover></i></a>
</template>
</b-table>
</div>
</div>
</div>
</template>
I tried a lot, after all this, my code is working correctly now, this given below code is the right solution.
<b-table ref="table" :items="products" :fields="fields" bordered hover :head-variant="'light'" responsive="sm" v-else>
<template v-slot:cell(index)="data">
{{ data.index+1 }}
</template>
<template v-slot:cell(name)="data">
<b-img thumbnail fluid :src="getImageUrl(data.item.image)" alt="Image 1" class="thumb-img"></b-img>
{{data.item.name}}
</template>
<template v-slot:cell(weights)="data">
<span v-weights="data.item"></span>
</template>
<template v-slot:cell(default_weight)="data">
<span v-floatFormatter="data.item.default_weight"></span>{{data.item.unit.sname}}
</template>
<template v-slot:cell(status)="data">
<span v-if="data.status" class="text-success text-bold">Active</span>
<span class="text-danger" v-else>Inactive</span>
</template>
<template v-slot:cell(action)="data">
<a #click.prevent="editItem(data.item.id)"><i class="fa fa-edit" aria-hidden="true" title="Update product" v-b-tooltip.hover></i></a>
<a #click.prevent="assignWeights(data.item.id)"><i class="fa fa-balance-scale" title="Assign weights" aria-hidden="true" v-b-tooltip.hover></i></a>
</template>
</b-table>

Vue v-for is displaying more than one image on click

I am using vue to show a photo that is being pulled from a mysql DB.
I loop through the images but when I click to show, all the images show instead of just the one that I want to show (the single image button I clicked). How do I get only one image to show up at a time?
UPDATED
<div>
<div v-for="(image, index) in images" :key="index">
<div class="dropdown">
<div>
<a class="dropdown-item preview-link" #click="togglePreview(index)">
Preview
</a>
</div>
</div>
<div v-if="currentIndex === index" class="modal-dialog modal-dialog-centered modal-md" role="image" style="max-width: 700px;">
<div class="modal-content" style="overflow: hidden;">
<div class="modal-header">
<h5 class="modal-title">{{image.title}}</h5>
<button type="button" class="close modal-close-button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<img :src="'/storage/images/' + image.name + '.jpg'">
</div>
</div>
</div>
</div>
</div>
In my vue methods:
togglePreview(index) {
this.currentIndex = this.currentIndex === index?-1:index
},
Vue Data
data() {
return {
currentIndex: -1,
}
},

VUEJS: Model not changing in for loop

The V-model:"question.answer" is the same for each loop.
The content of rating_questions is:
rating_question = [
{
"id":1,
"question":"How did you like this?",
"amount_of_stars":8,
"answer":0
},
{
"id":2,
"question":"Second question?",
"amount_of_stars":3,
"answer":0
}]
When I select an answer for the first question, the answer is saved in rating_question[0].answer but if I select an answer for the second question, it is also saved in rating_question[0].answer and not in rating_questions[1].answer as I would expect.
<template>
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="i" name="rating" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="i" >☆</label>
</span>
{{ rating_questions[index]['answer'] }}
{{index}}
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user', 'event', 'rating_questions'],
methods: {
sendRating() {
this.$emit('ratingsent', {
rating_questions: this.rating_questions
});
}
}
}
</script>
Your problem is not in Vue usage but how you use <input> and <label> HTML elements...
<input> id and <label> for attributes are assigned with simple number
1..question.amount_of_stars...which means first combo for every question will have id = 1, second 2 etc. Moreover you are using same name for every combo!
Now if you click on the label (star) in second question, browser just switch active combo on 1st question.
Try this:
<input :id="`rating-${question.id}-${i}`" :name="`rating-${question.id}`" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="`rating-${question.id}-${i}`" >☆</label>
Now:
every combo in the group (single question) will have same name (OK!)
every combo (and it's corresponding label) will have different id (OK!)
Also it's usually better to use :key together with v-for
new Vue({
data() {
return {
rating_questions: [
{
"id":1,
"question":"How did you like this?",
"amount_of_stars":8,
"answer":0
},
{
"id":2,
"question":"Second question?",
"amount_of_stars":3,
"answer":0
}]
}
}
}).$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions" :key="question.id">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="`rating-${question.id}-${i}`" :name="`rating-${question.id}`" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="`rating-${question.id}-${i}`" >☆</label>
</span>
{{ question.answer }}
<div class="clear"></div>
</div>
</div>
</div>
</div>
</div>
The error was in my input id's...
Each new question had the same id's.
<template>
<div class="ratings">
<div class="rating" v-for="(question, index) in rating_questions">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars">
<input :id="'rating' + index + i" name="rating" v-model="question.answer" type="radio" :value="i" class="radio-btn hide" />
<label :for="'rating' + index + i" >☆</label>
</span>
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ['user', 'event', 'rating_questions'],
methods: {
sendRating() {
this.$emit('ratingsent', {
rating_questions: this.rating_questions
});
}
}
}
</script>
There is very trivial fix of this problem.
See you are modifying the props. In vuejs, props are not supposed to be updated. I hope you might have encountered some error in the console (not sure though, as sometimes in my case also it doesn't appear)
Please use this SFC file
<template>
<div class="ratings">
<!-- using the data variable rather than props -->
<div class="rating" v-for="(question, index) in questions" :key="index">
<div class="question">
{{ question.question }}
</div>
<div class="answer">
<div class="rating-stars">
<span v-for="i in question.amount_of_stars" :key="i">
<input
:id="i"
name="rating"
v-model="question.answer"
type="radio"
:value="i"
class="radio-btn hide"
/>
<label :for="i">☆</label>
</span>
{{ questions[index]["answer"] }}
{{ index }}
<div class="clear"></div>
</div>
</div>
</div>
<span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" #click="sendRating">
Send
</button>
</span>
</div>
</template>
<script>
export default {
props: ["user", "event", "rating_questions"],
data() {
return {
questions: this.rating_questions, // because you can not change props in vuejs
};
},
methods: {
sendRating() {
this.$emit("ratingsent", {
rating_questions: this.questions,
});
},
},
};
</script>