how to iterate articles with images inside with vue - vue.js

I have to iterate some articles which contain images inside. The thing is that it doesn't work what I'm doing.
I have tried an v-for inside the article ad then, binding the src like this:
<section class="articulos b-flex b-flex-wrap b-flex-center b-flex-center-horizontal">
<article v-for="imagen in imagenes">
<figure class="contenedor-articulo">
<img v-bind:src="'../assets/img/' + imagen.url" />
<figcaption>
<h3>Ensaladilla Rusa</h3>
<p>+ 0,50 €</p>
<i class="fas fa-plus-circle"></i>
</figcaption>
</figure>
</article>
</section>
the script of the component is like this:
<script>
export default {
name: 'Articulos',
data() {
return {
imagenes: [
{url:"apeteat_2019_ensaladaquinoachicken.jpg"},
{url:"apeteat_2017__ensaladilla_rusa.jpg"},
{url:"apeteat_2018_nigirimix.jpg"},
{url:"apeteat_2019_wrapcesar.jpg"},
{url:"apeteat_2019_ensaladaquinoachicken.jpg"},
{url:"apeteat_2018_nigirimix.jpg"}
]
}
}
}
</script>
I have this error message:
message: error: Elements in iteration expect to have 'v-bind:key' directives (vue/require-v-for-key) at src/components/Articulos.vue:4:9:
2 |
3 | <section class="articulos b-flex b-flex-wrap b-flex-center b-flex-center-horizontal">
> 4 | <article v-for="imagen in imagenes">
| ^
5 |
6 | <figure class="contenedor-articulo">
the image is not displaying. Tha image url is so:
src="../assets/img/apeteat_2019_ensaladaquinoachicken.jpg", before iterating thwy were displaying perfectly.

Add unique key when use v-for.
<article
v-for="(imagen, index) in imagenes"
:key="index"
>
Using index as key is not recommended. Please add unique key or id in each image object like below.
[
{
id: 1,
url:"2019_ensaladaquinoachicken.jpg"
},
...
]
...
<article
v-for="imagen in imagenes"
:key="imagen.id"
>

I guess the problem with your imagenes array.
Instead of this (array with one object with duplicated keys):
imagenes: [{
url:"2019_ensaladaquinoachicken.jpg",
url:"2017__ensaladilla_rusa.jpg",
url:"2018_nigirimix.jpg",
url:"2019_wrapcesar.jpg",
url:"2019_ensaladaquinoachicken.jpg",
url:"2018_nigirimix.jpg"
}]
It most likely should be this (array of objects):
imagenes: [
{ url:"2019_ensaladaquinoachicken.jpg" },
{ url:"2017__ensaladilla_rusa.jpg" },
{ url:"2018_nigirimix.jpg" },
{ url:"2019_wrapcesar.jpg" },
{ url:"2019_ensaladaquinoachicken.jpg" },
{ url:"2018_nigirimix.jpg" }
]

You need to add a '/' in your path binding like this : v-bind:src="'../assets/img/apeteat_/' + imagen.url"
Also, rewrite your imagenes array to be like this (they key 'url' is duplicated):
imagenes: [
{url: //img name },
{url: //img name }
//etc
]

In HTML
<article v-for="imagen in imagenes" :key="imagen.id">
<img :src="require(`#/assets/${imagen.imagenes}`)" />
...
</article>
In JS
imagenes: [
{id:1, url:"apeteat_2019_ensaladaquinoachicken.jpg"},
{id:2, url:"apeteat_2017__ensaladilla_rusa.jpg"},
{id:3, url:"apeteat_2018_nigirimix.jpg"},
{id:4, url:"apeteat_2019_wrapcesar.jpg"},
{id:5, url:"apeteat_2019_ensaladaquinoachicken.jpg"},
{id:6, url:"apeteat_2018_nigirimix.jpg"}
]

Related

Is there a way to neaten up deeply nested objects during a v-for loop?

I'm currently working on a project in Vue that uses the WP-Rest api. I'm bored of tediously nested objects when looping through objects. While I can destructure top-level objects, I'm not sure how make things like this: page.page.featuredImage.node.altText less ugly. Can anyone offer any good practices for handling nested objects within Vue loops?
<div v-for="(page, index) in pageGrid" :key="index"
class="flex flex-col overflow-hidden rounded-lg shadow-lg">
<div v-if="page.page.featuredImage" class="flex-shrink-0">
<img class="h-48 w-full object-cover" :alt="page.page.featuredImage.node.altText"
:sizes="page.page.featuredImage.node.sizes" :srcset="page.page.featuredImage.node.srcSet" />
Sometimes that is why to do it in the other hand you can map over your data when mounted for instant and return a new array that looks the way you want.
I am not sure how your data looks but I think you could also do something like this:
<div v-for="(page, index) in pageGrid.page" :key="index">
As per your template code, You are having below data structure contains by pageGrid.
[
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image src link'
}
}
}
},
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image src link'
}
}
}
},
...
...
]
To make this simple, You can move the whole node object into a separate array with the help of Array.map() method and then it will easy to iterate in the template.
pageGrid.map(({ page }) => page.featuredImage.node);
Live Demo :
new Vue({
el: '#app',
data: {
pageGrid: [
{
page: {
featuredImage: {
node: {
altText: 'Text 1',
sizes: 'small',
srcSet: 'image 1 src link'
}
}
}
},
{
page: {
featuredImage: {
node: {
altText: 'Text 2',
sizes: 'medium',
srcSet: 'image 2 src link'
}
}
}
}
],
updatedPageGrid: []
},
mounted() {
this.updatedPageGrid = this.pageGrid.map(({ page }) => page.featuredImage.node);
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(page, index) in updatedPageGrid" :key="index">
{{ page.altText }}
{{ page.sizes }}
{{ page.srcSet }}
</div>
</div>

v-bind:src property not working with v-for

For some reason the imageURL property is not working, even though the url is correct, with the v-bind:src attribute.
The template:
<div v-for="project in projects" :key="project.id">
<section class="project-wrapper">
<img :src="project.imageURL" alt="Project image"/> //THE PROBLEM
<h1>{{ project.title }}</h1>
<p>{{ project.about }}</p>
<router-link :to="{ name: project.routeName }" class="type-two-link">ver projeto</router-link>
</section>
</div>
The script:
data() {
return {
projects: [
{
id: this.projectId,
title: "Minimalist Portfolio",
about: "...",
imageURL: "./minimalist-porfolio-desktop.png", //THE PROBLEM
routeName: "Minimalist Portfolio",
},
],
};
},
What am I doing wrong?
Any help is welcome.
I've tried to use 'required()' in the template, but it didn't worked.
So, this is how I solved it:
data() {
return {
projects: [
{
"...",
imageURL: require("../../assets/images/projects/minimalist-portfolio/minimalist-porfolio-desktop.png"),
routeSlugName: "My Portfolio",
},
],
};
},
,
You can learn more here: Static image src in Vue.js template

How to bind(v-model) data when a list with radio buttons in each list in Vue JS

I am fetching data from API. My data looks like -
[
{
id:1,
name:nameOfTheGroup1,
participants:[
{
id:1,
name:participant1
},
{
id:2,
name:participant2
}
]
},
{
id:2,
name:nameOfTheGroup2,
participants:[
{
id:3,
name:participant1
},
{
id:4,
name:participant2
}
]
}
]
As you can see its an array of objects. and in each object has nested array of objects. Basically i am trying to fetch all the groups for current user with its participants.
Now i am showing those in the browser using v-for like this -
<h3>Please assign an admin to given groups</h3>
<div v-for="group in groups">
{{ group.name }}
<div v-for="participant in group.participants">
<input type="radio" value="" v-model=""/>
<label>{{ participant.name }} </label>
</div>
</div>
Now, my question is how can i bind this data using v-model to get object/array with
group id and assigned user (radio checked ).
This is my best how i could explain))
Thanks.
First of all, your model doesn't have any field for the assigned participant. So, you need to add something like that:
id:1,
name:'nameOfTheGroup1',
assignedId: '',
participants:[
{
id:1,
name:'participant1'
},
{
id:2,
name:'participant2'
}
]
},
{
id:2,
name:'nameOfTheGroup2',
assignedId: 3,
participants:[
{
id:3,
name:'participant1'
},
{
id:4,
name:'participant2'
}
]
Then you need to provide binding:
<div v-for="participant in group.participants">
<input type="radio" v-model="group.assignedId" :value="participant.id" :name="group.id"/>
<label>{{ participant.name }} </label>
</div>
Do not forget to add "name" attribute to the radio.
Working example is here https://jsfiddle.net/7x46mtr1/
Try to use a property on participant.
Something like this
v-model=“participant.status”

Vue: add multiple classes inside a v-for, based on array

I need to add classes to an element, based on categories. Here is my example data:
data() {
return {
projects: [
{
title: 'Project 1',
categories: [{ name: 'featured' }, { name: 'category-1' }],
},
{
title: 'Project 2',
categories: [{ name: 'category-2' }],
},
],
};
},
What I need is to add the categories directly as classes on the wrapper div (with the v-for), that will render:
<div class="featured category-1">
<h3>Project 1</h3>
</div>
<div class="category-2">
<h3>Project 1</h3>
</div>
I'm not sure how to do this?
<div v-for="(project, index) in projects" :class="v-for="(category, index) in project.categories???">
{{project.title}}
</div>
Should I do this differently? I can't seem to figure it out. Thanks for your help!
It's simple:
<div v-for="project in projects" :class="classExtraction(project)">
<h3>
{{project.title}}
</h3>
</div>
You need a method that extracts classes from your project's categories:
methods: {
classExtraction(item) {
return item.categories.map(cat => cat.name);
}
}
http://jsfiddle.net/eywraw8t/371192/
Also, please note that you should use :key directive with v-for binding it to a unique property, preferably object's id:
https://v2.vuejs.org/v2/style-guide/#Keyed-v-for-essential

Checked item form Array 1, and pass checked item to Array 2, then hide checked item from Array 2 rendered list

First, please check this pen I found, the concept is similar to my question, ReactJS - Baby Name Inspiration. I hope to make it via Vue.js but sorry I don't know React.
The question I want to ask if the user click the list item from Array 1, I named Array 1 as animals, the structure will show below. Then, it will pass the clicked item to Array 2, Array 2 as wished_pets_list. If for example {displayName: "Kitty", value: "cat"} clicked from animals list, animals & wished_pets_list also stored this object. When the same object in two arrays, the render of animals element will hide the object's output in HTML; it also renders to wished_pets_list as button. If click wished_pets_list's item button, it will remove the object data from wished_pets_list, and can access back on animals HTML list. And it can loop again.
The setting of data, default:
data: () => ({
animals: [
{displayName: "Kitty", value: "cat"},
{displayName: "Puppy", value: "dog"},
{displayName: "Chick", value: "bird"},
{displayName: "Fawn", value: "Deer"},
{displayName: "Joey", value: "Kangaroo"},
{displayName: "Piglet", value: "pig"},
{displayName: "Fry", value: "fish"},
{displayName: "Polliwog", value: "frog"}
],
wished_pets_list: [],
wished_pets_list_formatted: []
}),
Something I try on make it as HTML:
<div v-for="item in wished_pets_list">
<span #click="removeSelected(item.value)">{{item.displayName}}</span>
</div>
<div class="dropdown-list-container">
<div class="dropdown-list" v-for="(item, index) in animals">
<label :for="'givenID' + item.index" #click="pushSelect(item.value)">{{index}}{{item.displayName}}</label>
<input type="checkbox" v-model="wished_pets_list" :value="{'displayName': item.displayName, 'value': item.value}" :id="givenID' + item.index">
</div>
</div>
<!-- a hidden text field to submit the formatted as value only -->
<input type="text" v-model="wished_pets_list_formatted" name="anyName" v-show>
Two methods I think it should use:
methods: {
removeSelected(value){
this.wished_pets_list_formatted.push(value);
},
pushSelect(value){
this.wished_pets_list_formatted.splice(value);
}
},
Thanks, if you can, please make a similar codepen or jsfiddle.
Below is a implementation of the example codepen in Vue(didn't included the search part because I think it's irelevant in this case).
The template:
<div id="app">
<div data-reactroot="">
<main>
<div class="favourites">
<h4>Your Shortlist</h4>
<ul>
<li class="girl" v-for="(animal, index) in wished_pets_list" #click="removeFromList(index)">{{animal.displayName}}</li>
</ul>
<hr>
</div>
<ul>
<li v-for="(animal, index) in animals" :key="animal.value" class="boy" #click="addToList(index)">{{animal.displayName}}</li>
</ul>
</main>
</div>
</div>
The javascript part:
var vm = new Vue({
el: "#app",
data () {
return {
animals: [
{displayName: "Kitty", value: "cat"},
{displayName: "Puppy", value: "dog"},
{displayName: "Chick", value: "bird"},
{displayName: "Fawn", value: "Deer"},
{displayName: "Joey", value: "Kangaroo"},
{displayName: "Piglet", value: "pig"},
{displayName: "Fry", value: "fish"},
{displayName: "Polliwog", value: "frog"}
],
wished_pets_list: [],
wished_pets_list_formatted: []
}
},
methods: {
addToList(index) {
this.wished_pets_list.push(this.animals[index])
this.animals.splice(index, 1)
},
removeFromList(index) {
this.animals.push(this.wished_pets_list[index])
this.wished_pets_list.splice(index, 1)
}
}
});
For the CSS you can use the one from the codepen example.
Codepen fork
Base on #Allkin's answer, and my addition requirement, I tried to make such like Allkin's answer with an ordered list.
The template:
<div id="app">
<div>
<div class="favourites">
<h4>Your Shortlist</h4>
<ul>
<li class="girl" v-for="(animal, index) in wished_pets_list" #click="removeFromList(index, animal.value, animal.id)">{{animal.displayName}}</li>
</ul>
<hr>
</div>
<ul>
<li v-for="(animal, index) in animals" :key="animal.value" class="boy" #click="addToList(index, animal.value, animal.id)" v-show="!animal.checked">{{animal.displayName}}</li>
</ul>
<span>wished_pets_list_formatted:</span>
<div>{{wished_pets_list_formatted}}</div><br><br>
<span>animals:</span>
<div>{{animals}}</div>
</div>
</div>
js:
var vm = new Vue({
el: "#app",
data() {
return {
org_animal_list: [
{ displayName: "Kitty", value: "cat" },
{ displayName: "Puppy", value: "dog" },
{ displayName: "Chick", value: "bird" },
{ displayName: "Fawn", value: "Deer" },
{ displayName: "Joey", value: "Kangaroo" },
{ displayName: "Piglet", value: "pig" },
{ displayName: "Fry", value: "fish" },
{ displayName: "Polliwog", value: "frog" }
],
animals: null,
wished_pets_list: [],
wished_pets_list_formatted: []
};
},
methods: {
addToList(index, value, id) {
console.log("added: " + value);
this.wished_pets_list.push(this.animals[index]);
this.wished_pets_list_formatted.push(value);
this.animals[index].checked = !this.animals[index].checked;
},
removeFromList(index, value, id) {
var self = this;
this.wished_pets_list.splice(index, 1);
this.wished_pets_list_formatted.forEach(function(item, index) {
if (item == value) {
self.wished_pets_list_formatted.splice(index, 1);
}
});
for (var i = 0; i < this.animals.length; i++) {
if (self.animals[i].id == id) {
self.animals[i].checked = !self.animals[i].checked;
}
}
}
},
beforeMount: function() {
this.animals = this.org_animal_list;
for (var i = 0; i < this.animals.length; i++) {
this.$set(this.animals[i], "checked", false);
this.$set(this.animals[i], "id", i);
}
}
});
I added an original list first, then it will clone before Vue mount. This action allows the developer can use back the original data for other use.
For the full example, please check on codepen