Vue js toggle class inside a loop - vue.js

I have a template like this :
<div id="vue-instance">
<ul>
<li v-for="item in inventory" v-on:click="say()" v-bind:class="{active:isActive}" > {{ item.name }} - ${{ item.price }}
</li>
</ul>
</div>
And from my "controller" :
var vm = new Vue({
el: '#vue-instance',
data: {
inventory: [
{name: 'MacBook Air', price: 1000},
{name: 'MacBook Pro', price: 1800},
{name: 'Lenovo W530', price: 1400},
{name: 'Acer Aspire One', price: 300}
],
isActive : false
},
methods: {
say: function () {
this.isActive = !this.isActive
}
}
});
With this when i click one item from the list, all items are affected to active class. My question is how to toggle individual element?
Thank you.

You need a active flag on each item to be able to track them individually.
For example:
{name: 'MacBook Air', price: 1000, isActive: false},
{name: 'MacBook Pro', price: 1800, isActive: true}
You will then will be able to bind the class and click event to item.isActive by doing something like this:
<li v-for="item in inventory" v-on:click="item.isActive = !item.isActive" v-bind:class="{active:item.isActive}" > {{ item.name }} - ${{ item.price }}
</li>

Notice that if class name contains dash "-" vue can't comile the template.
I tried with class with this name "main-container" and face this issue.

Related

How v-for works in vue3

I have this code in a vue3 template:
<a v-if="card.type == 'link'" :href="card.linkData.url">
<img v-if="card.linkData.image" :src="card.linkData.image">
<img v-else :src="card.linkData.favicon">
</a>
The code gets an array of cards for firebase. They might be of type 'image', 'note' or 'link'. By iterating through the array with a v-for, It renders every card with it's info.
If they are of type 'link', they contain an object called linkData.
I want the bit of code shown above to show up only if the card is of type 'link'.
But, I get an error saying that card.linkData is undefined when it's rendering cards of type 'image' or 'note'.
Is that normal? Does it need to read card.linkdata even though it won't render it?
I tried doing this:
<div v-if="card.type == 'link'">
<a :href="card.linkData.url">
<img v-if="card.linkData.image" :src="card.linkData.image">
<img v-else :src="card.linkData.favicon">
</a>
</div>
but the error is still there.
What would be the proper way to do this?
these are examples of card objects obtained fro firebase:
{
id: 1599762353,
imageName: '',
imageUrl: '',
thumbnail: 'link to thumbnail',
title: 'hello',
content: 'a string of text',
createdAt: 'date',
type: 'note'
}
{
id: 1599765625,
imageName: '',
imageUrl: '',
thumbnail: 'link to thumbnail',
title: 'hello',
content: 'a string of text',
createdAt: 'date',
type: 'link',
linkData: {
link: 'https//:www.userlink.com',
image: 'https//:www.linkimage.com',
favicon: ''https//:www.linkicon.com'
}
}
I also tried giving a 'linkData: null' property to cards of type 'image' and 'note' but now the error is card.linkData is null.
Try like following snippet (v-if="card.linkData && card.linkData.link">):
new Vue({
el: '#demo',
data() {
return {
items: [
{id: 1599762353, imageName: '', imageUrl: '', thumbnail: 'link to thumbnail', title: 'hello note', content: 'a string of text', createdAt: 'date', type: 'note'},
{id: 1599765625, imageName: '', imageUrl: '', thumbnail: 'link to thumbnail', title: 'hello', content: 'a string of text', createdAt: 'date', type: 'link', linkData: {link: 'https//:www.userlink.com', image: 'https://picsum.photos/70', favicon: 'https//:www.linkicon.com'}},
{id: 1599765626, imageName: 'https://picsum.photos/50', imageUrl: '', thumbnail: 'link to thumbnail', title: 'hello', content: 'a string of text', createdAt: 'date', type: 'link',}
]
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<li v-for="card in items" :key="card.id">
<div v-if="card.type == 'link'">
<a :href="card.linkData" v-if="card.linkData && card.linkData.link">
<img v-if="card.linkData.image" :src="card.linkData.image">
<img v-else :src="card.linkData.favicon">
</a>
<a :href="card.linkData" v-else>
<img :src="card.imageName">
</a>
</div>
<div v-else>
<p>{{ card.title }}</p>
</div>
</li>
</div>

vue #click not changing class

sorry I'm still new to reactive vue models and updating I'm trying to draw a line through an input element if a box is checked.
so far I have this setup:
data: {
selected: null,
checked: null,
list: [
{
id: 0,
category: 'Bakery',
food: ['bread','muffins','pie']
},
{
id: 1,
category: 'Fruits',
food: ['apple','bananna','orange']
},
{
id: 2,
category: 'Diary',
food: ['cheese','milk','yogurt']
}
],
isHidden: true,
form: {},
},
my html is as follows (this is a single page app)
<li v-for="food in item.food" class="list-group-item">
<input :class="{marked:food == checked}" #click="checked = food" type="checkbox"> {{ food }}</input>
</li>
this is the css i'm trying to implement
.marked{
text-decoration: line-through;
}
I'm not sure what I need to do to my #click to make it work but so far nothing happens and the class is not applied. Can someone give me tips?
An <input> element can't have content.
So you've got this structure:
<input>{{ food }}</input>
But that's misleading as the <input> tag will be closed automatically. What you'll end up with will actually be some text next to a checkbox, not inside it.
You probably want something similar to this:
<label :class="{marked: food === checked}" #click="checked = food">
<input type="checkbox"> {{ food }}
</label>
There are some other problems involving deselection and multiple selections. I've tried to fix those in the example below:
new Vue({
el: '#app',
data: {
checked: [],
list: [
{
id: 0,
category: 'Bakery',
food: ['bread','muffins','pie']
},
{
id: 1,
category: 'Fruits',
food: ['apple','bananna','orange']
},
{
id: 2,
category: 'Diary',
food: ['cheese','milk','yogurt']
}
]
}
})
.marked{
text-decoration: line-through;
}
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<ul v-for="item in list">
<li v-for="food in item.food" class="list-group-item">
<label :class="{marked: checked.includes(food)}">
<input type="checkbox" v-model="checked" :value="food">{{ food }}
</label>
</li>
</ul>
</div>

How do I reference an image path in an img src in Vue JS

I have an array of objects coming from a prop. Each object has a title and img key values. I'm using v-for to display the title and how the image from the img value.
<div v-for="item in products" :key="item.id">
<h1>{{item title}}</h1>
<img :src="item.img">
</div>
export default {
name: "home",
props: ["products"]
/*
here is the products
[{id: 1, title: "Moe", img: "../assets/images/stooges/moe.jpg"},
{id: 2, title: "Larry", img: "../assets/images/stooges/larry.jpg"},
{id: 3, title: "Curly", img: "#/assets/images/stooges/curly.jpg"}]
*/
};
On the last element, I'm trying the relative referencing. I've also tried something like this
<img :src="require(item.img)">
At least for the last element, I was hoping to see the image.
<div v-for="item in products" :key="item.id">
<h1>{{ item.title }}</h1>
<img :src="require(`#/assets/images/stooges/${item.img}.jpg`)" />
</div>
export default {
name: "home",
props: ["products"]
data() {
return {
products: [
[{id: 1, title: "Moe", img: "moe"},
{id: 2, title: "Larry", img: "larry"},
{id: 3, title: "Curly", img: "curly"}]
]
};
},
};

Binding model attribute to radio button with vuejs

I have a dataset that looks like this:
[
{id: 1, name: 'Foo', is_primary: false},
{id: 2, name: 'Bar', is_primary: true},
{id: 3, name: 'Baz', is_primary: false},
]
Only one of the entries is allowed to have is_primary = true. I'm displaying these items in a list, and I'm trying to display a radio button for each that the user can select to indicate that this is the primary one.
<tr v-for="item in items">
<td><input name="primary" type="radio" v-model="item.is_primary"></td>
</tr>
However, I don't think I'm understanding how this is supposed to work, because it's not working for me. Is this possible or am I supposed to handle this situation another way?
A set of radio inputs should v-model the same variable: a scalar that takes on the value associated with the selected radio.
To translate that back and forth into your item list, you can use a settable computed.
new Vue({
el: '#app',
data: {
items: [{
id: 1,
name: 'Foo',
is_primary: false
},
{
id: 2,
name: 'Bar',
is_primary: true
},
{
id: 3,
name: 'Baz',
is_primary: false
},
]
},
computed: {
primaryItem: {
get() {
return this.items.find((i) => i.is_primary);
},
set(pi) {
this.items.forEach((i) => i.is_primary = i === pi);
}
}
}
});
<script src="https://unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<div v-for="item in items">
<input name="primary" type="radio" :value="item" v-model="primaryItem">
</div>
<pre>{{JSON.stringify(items, null, 2)}}</pre>
</div>

How to get Vue v-for list rendering working with Blade view in Laravel

I'm practicing Vue list rendering with Laravel, and can't get the data to display in my blade view with the v-for directive.
I've tried various examples from the Internet. I have a simple list with a few items that I want to display in a list, but instead of displaying the contents, I receive the item name displayed to the screen like {{ item.name }}
This is the relevant portion in my item.blade.php view file:
<ul>
<li v-for="item in inventory">
#{{ item.name }}
</li>
</ul>
</div>
#section('scripts.app.footer')
<script type="text/javascript" src="{{ mix('js/app2.js') }}"></script>
#endsection
And this is from app2.js (don't need a .vue file yet since I haven't gotten to creating templates yet)
var vm = new Vue({
el: '#vue-instance',
data: {
inventory: [
{name: 'MacBook Air', price: 1000},
{name: 'MacBook Pro', price: 1800},
{name: 'Lenovo W530', price: 1400},
{name: 'Acer Aspire One', price: 300}
]
}
});
All I'm getting in my browser is {{ item.name }} I would expect to see a list of the 4 item names. I'm not sure where I'm going wrong, as I've looked at other examples and one of my other team projects is working just fine with v-for (using .js files, not .vue files), but I can't get a simple list to display. Any help would be appreciated. npm run dev is working fine as well.
Data must return a data object data:()=>{ return { a:'stuff' } }
<template>
<div>
<ul>
<li v-for="item in inventory" :key="item">#{{ item.name }}</li>
</ul>
</div>
</template>
<script>
export default {
data: () => {
return {
inventory: [
{ name: "MacBook Air", price: 1000 },
{ name: "MacBook Pro", price: 1800 },
{ name: "Lenovo W530", price: 1400 },
{ name: "Acer Aspire One", price: 300 }
]
};
}
};
</script>
sandbox