Quick question. If I have data in an element in the form of an array as follow:
data: {
product: socks,
variants: [
{
variantId: 2234,
variantColor: 'Green',
variantQuantity: 0,
},
{
variantId: 2235,
variantColor: 'Blue',
variantQuantity: 10,
}
}
how can I select a certain variantQuantity based on me hovering over a certain div?
Full code:
HTML:
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ product }}</h1>
<p v-if="inStock">In Stock</p>
<p v-else :class="{ outOfStock: !inStock }">Out of Stock</p>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<div class="color-box"
v-for="variant in variants"
:key="variant.variantId"
:style="{ backgroundColor: variant.variantColor }"
#mouseover="updateProduct(variant.variantImage)"
>
</div>
<button v-on:click="addToCart"
:disabled="!inStock"
:class="{ disabledButton: !inStock }"
>
Add to cart
</button>
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
</div>
Javascript:
var app = new Vue({
el: '#app',
data: {
product: 'Socks',
image: 'https://dl.dropboxusercontent.com/s/9zccs3f0pimj0wj/vmSocks-green-onWhite.jpg?dl=0',
inStock: false,
details: ['80% cotton', '20% polyester', 'Gender-neutral'],
variants: [
{
variantId: 2234,
variantColor: 'green',
variantImage: 'https://dl.dropboxusercontent.com/s/9zccs3f0pimj0wj/vmSocks-green-onWhite.jpg?dl=0',
variantQuantity: 0
},
{
variantId: 2235,
variantColor: 'blue',
variantImage: 'https://dl.dropboxusercontent.com/s/t32hpz32y7snfna/vmSocks-blue-onWhite.jpg?dl=0',
variantQuantity: 10
}
],
cart: 0
},
methods: {
addToCart() {
this.cart += 1
},
updateProduct(variantImage) {
this.image = variantImage
}
}
})
You could include variant.variantQuantity in the mouseover event-handler expression:
<div v-for="variant in variants"
#mouseover="updateProduct(variant.variantImage, variant.variantQuantity)"
>
Also add a data property for quantity, and update the handler to accommodate the new property:
data() {
return {
quantity: 0,
// ...
};
},
methods: {
updateProduct(variantImage, variantQuantity) {
this.image = variantImage;
this.quantity = variantQuantity;
},
// ...
}
demo based on your codepen
Related
I have an array of names using v-for I get the names, as you can see I have two v-fors where the content is duplicated in this example my content is small and doesn't look so scary in real life it can be much bigger and all the problem is that the content is repeated, I tried to apply slots but could not cope
Template
<template>
<div>
<div v-for="(item, index) in array" :key="index" class="names">
<div class="show-names">
<p>{{ item.name }}</p>
</div>
<div
v-for="(girlNames, index) in item.girlNames"
:key="index"
class="names"
>
<div class="show-names">
<p>{{ girlNames.name }}</p>
</div>
</div>
</div>
</div>
</template>
Script
<script>
export default {
data() {
return {
array: [
{ name: "Alex" },
{ name: "Jacob" },
{ name: "Robert" },
{
girlNames: [
{
name: "Anna",
},
{
name: "Kiwi",
},
{
name: "Ava",
},
],
},
],
};
},
};
</script>
Yes, this picture shows where the content is repeated
You can also see code example in codesandbox
The only problem I see here is bad data structure. In my opinion it should be an object with two fields, which seperate in your case boys and girls, and in this object should be actual data:
<script>
export default {
data() {
return {
names: {
boys: [
{ name: "Alex" },
{ name: "Jacob" },
{ name: "Robert" },
],
girls: [
{ name: "Anna" },
{ name: "Kiwi" },
{ name: "Ava" },
]
}
},
],
};
},
};
</script>
They your code in template will be like:
<template>
<div>
<div class="names">
<div v-for="(item, index) in name.boys" :key="index" class="show-names">
<p>{{ item.name }}</p>
</div>
<div v-for="(item, index) in name.girls" :key="index" class="show-names">
<p>{{ item.name }}</p>
</div>
</div>
</div>
</template>
I am really confused.. I have a list of items with a heart-like "like" button. After the icon's clicked on I would like to change the color. But after this all my hearts change their colors, not only the one, which I pressed. Should I pass some argument in markAsFavorite method? Like index, or book?
<template>
<v-flex v-for="(book, index) in allBooks">
<div>Title: {{ book.title }}</div>
<i #click="markAsFavorite()" :class="{isActive: isMark}" class="fas fa-heart"></i>
</template
<script>
name: 'Books',
data () {
return {
allBooks: [
{ title: "one" },
{ title: "two" },
{ title: "three" },
{ title: "four" },
],
isMark: false,
}
},
methods: {
markAsFavorite() {
this.isMark = !this.isMark
},
}
</script>
Every object should have it property isMark as below:
UPDATE: you can merge properties to your original data after be fetched:
const myData = [
{ title: "one"},
{ title: "two" },
{ title: "three" },
{ title: "four" },
]
new Vue({
el: '#app',
data() {
return {
isLoading: true,
allBooks: []
}
},
created() {
this.fetchAPI()
},
methods: {
fetchAPI() {
setTimeout(() => {
this.allBooks = myData.map(item => ({...item, isMark: false }))
this.isLoading = false
}, 2000)
},
markAsFavorite(book) {
book.isMark = !book.isMark
}
}
})
.my-icon {
cursor: pointer;
margin-left: 4px;
}
.isActive {
color: red;
}
.flex {
display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/js/all.min.js"></script>
<div id="app">
<span v-if="isLoading">
loading
<i class="fas fa-spinner fa-spin"></i>
</span>
<template v-else>
<div class="flex" v-for="(book, index) in allBooks" :key="index">
<div>Title: {{ book.title }}</div>
<span
:class="[
{ isActive: book.isMark },
'my-icon'
]"
#click="markAsFavorite(book)"
>
<i class="fas fa-heart"></i>
</span>
</div>
</template>
</div>
I would separate out each book into its own component. That way it can track its own isMark
Book.vue
<template>
<div>
<div>Title: {{ book.title }}</div>
<i #click="markAsFavorite" :class="{isActive: isMark}" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
name: 'Book',
props: { book: Object },
data: () => ({ isMark: false }),
methods: {
markAsFavorite () {
this.isMark = !this.isMark
this.$emit(this.isMark ? 'marked' : 'unmarked', this.book)
}
}
}
</script>
and then in your Books component
<template>
<v-flex v-for="(book, index) in allBooks" :key="index">
<Book :book="book" #marked="handleMarked" #unmarked="handleUnmarked" />
</v-flex>
</template>
<script>
import Book from './Book.vue'
export default {
name: 'Books',
components: { Book },
// and so on
}
</script>
How can we highlight a item in a list of item when the particular item is clicked? Should we use id as reference?
<li v-for="todo in todos">
<label>
<a href="#"
v-on:click="toggle(todo)"
:style="{color:activeColor}"
>
{{ todo.text }}
</a>
</label>
</li>
toggle: function(todo){
this.activeColor = 'red'
}
I tried here:
https://jsfiddle.net/eywraw8t/110976/
You can add activeIndex to store current active index:
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="(todo, idx) in todos">
<label>
<a href="#"
v-on:click="toggle(idx)"
v-bind:checked="todo.done"
:class="{'active': idx == activeIndex}"
>
{{ todo.text }}
</a>
</label>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
activeColor:String,
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: false },
{ text: "Build something awesome", done: false }
],
activeIndex: null
},
methods: {
toggle: function(index){
this.activeIndex = index
}
}
and in css
.active {
color: red;
}
Demo: https://jsfiddle.net/Lv7eanru/
This is another solution to highlight selected item in a list using VueJS :
<div id="app">
<ul>
<li v-for="value in objectArray" v-on:click="highlight($event)" >
First name : {{ value.firstName }} -- Last name : {{ value.lastName }}
</li>
</ul>
and in JS file we have:
Vue.createApp({
data() {
return {
objectArray: [{
firstName: 'John',
lastName: 'Doe'
},
{
firstName: 'Amily',
lastName: 'Brown'
},
{
firstName: 'Jack',
lastName: 'London'
},
],
}
},
methods: {
highlight: function (event) {
for (var i = 0; i < event.target.parentElement.children.length; i++) {
event.target.parentElement.children[i].classList.remove('bg-warning');
}
event.target.classList.add('bg-warning');
}
},
}).mount('#app');
I try to add shopping cart,but I do not know how to do it. When count = 0,- is hidden.And when count > 0,- is show.When i try to click +, automatically increase 1, click - automatically reduced by 1. But can not be displayed.jsfiddle
Look at the Javascript file:
const goods = [{
id: "1",
goods_name: "水立方",
goods_price: "30.00",
goods_num: "15",
count:"0"
}, {
id: "2",
goods_name: "农夫山泉",
goods_price: "28.00",
goods_num: "10",
count:"0"
}]
var app = new Vue({
el: "#app",
data: {
list: goods,
},
methods: {
addCart(item,event) {
if (!this.item.count) {
Vue.set(this.item, 'count', 1);
} else {
this.item.count++;
}
},
lessCart(event) {
this.item.count--;
}
}
})
HTML file:
<div id="app">
<ul>
<li v-for="item in list">
<p>{{item.goods_name}}</p>
<p>{{item.goods_price}}</p>
<a v-show="item.count > 0" #click.stop.prevent="lessCart(item,$event)">-</a>
<input v-show="item.count > 0" v-model="item.count">
<a #click.stop.prevent="addCart(item,$event)">+</a>
</li>
</ul>
</div>
You are mutating the same state each time and not the state from the list.
You should simply do:
const goods = [{
id: "1",
goods_name: "水立方",
goods_price: "30.00",
goods_num: "15",
count:"0"
}, {
id: "2",
goods_name: "农夫山泉",
goods_price: "28.00",
goods_num: "10",
count:"0"
}]
var app = new Vue({
el: "#app",
data: {
list: goods,
},
methods: {
addCart(item) {
item.count++;
},
lessCart(item) {
item.count--;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<ul>
<li v-for="item in list">
<p>{{item.goods_name}}</p>
<p>{{item.goods_price}}</p>
<a v-show="item.count > 0" #click.stop.prevent="lessCart(item)">-</a>
<input v-show="item.count > 0" v-model="item.count">
<a #click.stop.prevent="addCart(item)">+</a>
</li>
</ul>
</div>
Note that you do not need the event argument in your method.
I'm trying to use a local filter with v-for but I'm getting an error
Property or method "filterByTitle" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property.
Code below
<template>
<div class="row">
<div class="col pt-5">
<ul class="blog-list-single" v-for="(post, index) in posts | filterByTitle" :key="index">
<li class="title">{{ post.title }}</li>
<li class="author">{{ post.author }}</li>
</ul>
</div>
</div>
</template>
<style lang="scss">
</style>
<script>
export default {
data() {
return {
posts: [
{ title: 'a', author: 'nd' },
{ title: 'b', author: 'nd' },
{ title: 'c', author: 'nd' },
],
selectedValue: 'a',
}
},
filters: {
filterByTitle(value) {
return value.filter(el => el.title == this.selectedValue)
}
},
}
</script>
Filters are limited in Vue 2 primarily to formatting string interpolations. You can also now use them in v-bind expressions.
In Vue 2, you would filter a list like this using a computed property.
console.clear()
new Vue({
el: ".row",
data() {
return {
posts: [{
title: 'a',
author: 'nd'
},
{
title: 'b',
author: 'nd'
},
{
title: 'c',
author: 'nd'
},
],
selectedValue: 'a',
}
},
computed: {
filterByTitle() {
// return the whole list if there is no filter value
if (!this.selectedValue) return this.posts
// otherwise return the list filtered by title
return this.posts.filter(el => el.title == this.selectedValue)
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div class="row">
<div class="col pt-5">
<ul class="blog-list-single" v-for="(post, index) in filterByTitle" :key="index">
<li class="title">{{ post.title }}</li>
<li class="author">{{ post.author }}</li>
</ul>
</div>
</div>