I have a vue version "^2.6.11" app with vuex where I am adding items to the cart. My items have variations like colour so I have two conditions before adding to cart comparing the colour and the product ID.
product.productId == item.productId && product.colour == item.colour
The colour is chosen from a select box, a property on the product is set with the selected colour on change event from the select box.
this.item.colour = event.target.value
The problem is that it updates the current item in the cart even if the colour is changed for example:
Select box displays gold, "add to cart" is clicked and a gold item is added to cart.
Select box is changed to silver, the product component changes its colour property to silver and "add to cart" is clicked.
The current item in the cart quantity changes to 2 and its colour changes to silver.
The odd thing is when I navigate to the checkout then click the back button, the add to cart works the way I want it to by adding the new variation of the item to the cart, however, when I add another variation after that it works as it did the first time.
I hope this makes sense. I don't understand why it doesn't work prior to click the back button.
I can't set up a Jsfiddle or code snippit because there are so many components working together here so I will add some of my code below.
// Product.vue
<template>
<div>
<b-col>
<b-card
v-bind:title="product_type"
v-bind:img-src="imagePath"
img-alt="Image"
img-top
tag="article"
style="max-width: 15rem;"
class="mb-2">
<b-card-text>{{ product_type }}</b-card-text>
<div class="form-group">
<label for="colourSelect">Please select a colour</label>
<select @change="onChange($event)">
<option v-for="item in metal_colour" v-bind:key="item">
{{ item }}
</option>
</select>
</div>
<div class="clearfix">
<div class="price pull-left" v-bind:key="price">€{{ price }}</div>
<div class="text-right">
<b-button @click="addToCart" variant="primary">Add to cart</b-button>
...
</template>
<script>
export default {
name: 'Product',
props: {
productId: Number,
imagePath: String,
product_type: String,
metal_colour: Array,
price: Number,
qty: Number
},
data() {
return {
item: {
productId: this.productId,
imagePath: this.imagePath,
product_type: this.product_type,
price: this.price,
colour: 'Gold',
qty: 1
}
}
},
methods: {
addToCart() {
this.$store.commit('addToCart', this.item)
this.$bvModal.show('modal-1')
},
onChange(event) {
this.item.colour = event.target.value
}
}
}
</script>
// store/index.js Vuex
...
mutations: {
addToCart(state, item) {
let found = state.inCart.find(
product =>
product.productId == item.productId && product.colour == item.colour
)
if (found) {
found.qty++
} else {
state.inCart.push(item)
}
}
},
...
For my shopping cart I am using a modal and the items are listed as follows:
// Cart.vue
<div>
<b-table striped :items="this.$store.state.inCart">
<template v-slot:cell(imagePath)="data">
<img :src="data.value" class="thumb" alt="product" />
{{ data.imagePath }}
</template>
</b-table>
</div>
<div slot="modal-footer">
<router-link to="/checkout">
<b-button variant="primary" @click="$bvModal.hide('modal-1')">
Checkout
</b-button>
</router-link>
<b-button class="modalBtn" variant="primary" @click="$bvModal.hide('modal-1')">
Continue Shopping
</b-button>
</div>
You used this
> this.$store.state.inCart
in the <template>
markup, should be just $store.state.inCart
but that should have flagged an error anyway and never actually work.
<b-table striped :items="$store.state.inCart">
You might be having reactivity issues, try this instead:
import {mapState} from 'vuex'
computed: {
...mapState(['inCart'])
}
<b-table striped :items="inCart">
Finally, When you add an item to your cart, you need to add a COPY of the item
object because you are passing the object by reference and getting unexpected behavior as a result of doing state.inCart.push(item)
.
There are two options:
1) import lodash
> import { copy } from 'lodash'
and use copy(item)
2) JSON.parse(JSON.stringify(item))
looks dirty but is fast.
These two approaches will de-reference the object.
To clarify, you can do this copy process inside your vuex
addToCart
method:
addToCart(state, item) {
let found = state.inCart.find(
product => product.productId === item.productId && product.colour === item.colour
)
if (found) {
found.qty++
} else {
// Clone the object.
const insert = JSON.parse(JSON.stringify(item))
state.inCart.push(insert)
}
}