If an already added product is again added to the cart then only cart number should increase but the function appends the data to the list and the product gets duplicate in the cart view. How to make sure check if the product already exists then only increment or decrement the count.
below is the code to update product
const initialState = {
cart: [],
total: 0,
}
const cartItems = (state = initialState, action) => {
switch(action.type) {
case 'ADD_TO_CART':
return {
...state,
cart: [action.payload, ...state.cart],
total: state.total + 1
}
// return [...state,action.payload]
case 'REMOVE_FROM_CART' :
return state.filter(cartItems => cartItems.id !== action.payload.id)
}
return state
}
A single item from the data will be like
{key:'1', type:"EARRINGS", pic:require('../../assets/earring.jpg'),price:"200"}
If you are using the same key for the items you can do like below
case 'ADD_TO_CART':
{
const updatedCart = [...state.cart];
const item = updatedCart.find(x=>x.key===action.payload.key);
if(item)
{
item.count++;
}
else{
updatedCart.push(action.payload);
}
return {
...state,
cart: updatedCart,
total: state.total + 1
}
}
The logic would search for items in the array and increase the count or add a new item to the array.
I think this will work.
case UPDATE_CART:
let receivedItem = action.payload
let itemList = state.cart
let stepToUpdate = itemList.findIndex(el => el.id === receivedItem.id);
itemList[stepToUpdate] = { ... itemList[stepToUpdate], key: receivedItem };
return { ...state, cart: itemList }
'id' is a unique thing to update specific item present in your cart. It cab be product id or some other id.
itemList.findIndex(el => el.id === receivedItem.id);
There are different ways of achieving this. You can create actions to INCREMENT/DECREMENT in case you know the product is added (eg: on the cart summary).
And you can also let this behaviour inside the ADD_TO_CART action if you don't know whether the product is added or not:
case "ADD_TO_CART": {
const isProductAdded = state.cart.find(
item => action.payload.id === item.id
);
return {
...state,
cart: isProductAdded
? state.cart.map(item => {
if (item.id === action.payload.id) {
item.qty++;
}
return item;
})
: [action.payload, ...state.cart],
total: state.total + 1
};
}
Related
I have following store defined:
state: () => ({
infoPackCreationData: null,
infoPackCreationTab: null,
}),
getters: {
infoPackImage(state: any) {
return state.infoPackCreationTab && state.infoPackCreationTab.infopackContents
? state.infoPackCreationTab.infopackContents.filter((item: any) => item.type === "IMAGE")
: [];
}
},
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
item = infopackImageData
console.log(item , 'this is items');
return item})
}
},
actions: {
setImageData(context: any, payload: any) {
context.commit('setImageData', payload)
}
}
and in my component I am using the computed to get the imageList:
computed: {
...mapGetters("creationStore", ["infoPackImage"]),
imageList: {
get() {
return this.infoPackImage ?? [];
},
set(value) {
this.$store.dispatch('creationStore/setImageData', value);
}
}
},
The problem is I want to edit a value of the imageList by index using draggable libarary,
but imageList does not act reactive and it just move the image and not showing the other image in the previous index:
async imageChange(e) {
this.loading = true
let newIndex = e.moved.newIndex;
let prevOrder = this.imageList[newIndex - 1]?.order ?? 0
let nextOrder = this.imageList[newIndex + 1]?.order ?? 0
const changeImageOrder = new InfopackImageService();
try {
return await changeImageOrder.putImageApi(this.$route.params.infopackId,
this.$route.params.tabId,
e.moved.element.id, {
title: e.moved.element.title,
infopackAssetRef: e.moved.element.infopackAssetRef,
order: nextOrder,
previousOrder: prevOrder,
}).then((res) => {
let image = {}
let infopackAsset = e.moved.element.infopackAsset
image = {...res, infopackAsset};
Vue.set(this.imageList, newIndex , image)
this.loading = false
return this.imageList
});
} catch (e) {
console.log(e, 'this is put error for tab change')
}
},
Array.prototype.filter doesn't modify an array in-place, it returns a new array. So this mutation isn't ever changing any state:
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents.filter((item: any) => {if(item.type === "IMAGE")
item = infopackImageData
console.log(item , 'this is items');
return item})
}
},
So, if you intend to change state.infoPackCreationTab.infopackContents, you'll need to assign the result of filter():
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab.infopackContents = state.infoPackCreationTab.infopackContents.filter(...)
However, since state.infoPackCreationTab did not have an infopackContents property during initialization, it will not be reactive unless you use Vue.set() or just replace the whole infoPackCreationTab object with a new one (see: Vuex on reactive mutations):
mutations: {
setImageData(state:any, infopackImageData: any) {
state.infoPackCreationTab = {
...state.infoPackCreationTab,
infopackContents: state.infoPackCreationTab.infopackContents.filter(...)
};
I created a custom gift box creator for a client. The current approach is that I created a "Gift Box" product and add the selected items as attributes on the cart item. This is all done in the theme's JS code but the problem I'm facing is that because I'm not actually adding the underlying items to the cart inventory is not updating.
Is there a way to not show the underlying items in the cart but have checkout update their inventory counts?
What if you had a microservice which responded to order/created and edited the order to include the items # $0?
You could use the steps outlined in this official Shopify tutorial on their GraphQL feature, and implemented in node:
import Shopify from 'shopify-api-node'; // https://github.com/MONEI/Shopify-api-node
shopify = new Shopify({/* your app credentials */});
// assume this is the HTTP endpoint w/ a JSON middleware
function(req, res) {
const order = req.body;
const giftBasket = order.line_items.find(li => li.id === 1234); // 1234, or whatever the product ID of your gift basket is.
const realItemSKUs = giftBasket.properties.filter((lineItemProp) => {
// assuming you use a product1: sku, product2: sku line item attribute name/value.
// but adjust to your needs.
return lineItemProp.name.match('product\d');
}).map((lineItemProp) => lineItemProp.value);
function createVariantQuery(sku) {
return `product${id}: {
productVariants(query: "sku:${sku}") { id }
}`;
}
const query = realItemSKUs.map((sku) => createVariantQuery(sku)).join('\n')
const variantIds = await shopify.graphql(`{${query}}`)
.then((variants) => Object.values(variants).map((variant) => variant.id));
const orderEditRes = await shopify.graphql(`mutation beginEdit{
orderEditBegin(id: "gid://shopify/Order/1234"){
calculatedOrder{
id
}
}
}`);
const calcLineItems = await Promise.all(variantIds.map(async (id) =>
shopify.graphql(`mutation addVariantToOrder{
orderEditAddVariant(id: "gid://shopify/CalculatedOrder/${orderEditRes.calculatedOrder}", variantId: "${id}", quantity: 1) {
calculatedOrder {
id
addedLineItems(first:5) {
edges {
node {
id
quantity
}
}
}
}
userErrors {
field
message
}
}
}`);
));
await Promise.all(calcLineItems.map(async (calcLineItem =>
shopify.graphql(`addDiscount {
orderEditAddLineItemDiscount(id: "${calculatedOrder.id}", lineItemId: "${calcLineItem.id}", discount: {percentValue: 100, description: "Giftbasket"}) {
calculatedOrder {
id
addedLineItems(first:5) {
edges {
node {
id
quantity
}
}
}
}
userErrors {
message
}
}
}`
);
return shopify.graphql(`mutation commitEdit {
orderEditCommit(id: "${calculatedOrder}", notifyCustomer: false, staffNote: "Auto giftbasket updated") {
order {
id
}
userErrors {
field
message
}
}
}`);
};
I get the data from the store like so
computed: {
notes() {
var data = this.$store.getters.getNotes;
var key = this.$store.getters.getTitleFilter;
if (key === "all") return data;
return data.filter((note) => {
var filteredNote = note.category.some(({ name }) => name === key);
if (filteredNote) return filteredNote;
});
},
},
When the note array changes (an item is removed, getNotes should reflect that. In other instances (where the data is returned without filtering) this used to do the trick:
watch: {
notes(newval) {
return newval;
},
},
I there a way to get the filtered array to update?
I'm learning vuejs and I'm doing a weather app, the goal is to rank cities with an index (humidex). I fetch weather information by API (axios) in order to collect data from several cities. I want to auto update data every x minutes, problem : some of my results are duplicated (the new data don't replace the old one).
I tried to set an unique key (based on latitude and longitude) for each item, it works for several results but not for all.
data () {
return {
items:[],
show: false,
cities: cities,
newCity:''
}
},
components: {
Item
},
computed: {
sortHumidex() {
return this.items.slice().sort((a,b) => {
return this.getHumidex(b) - this.getHumidex(a) || b.current.temp_c - a.current.temp_c
})
}
},
methods: {
addCity() {
if (this.newCity.trim().length == 0) {
return
}
this.cities.push(this.newCity)
this.newCity = ''
},
getHumidex: (el) => {
const e = 6.112 * Math.pow(10,(7.5*el.current.temp_c/(237.7+el.current.temp_c)))
*(el.current.humidity/100)
return Math.round(el.current.temp_c + 5/9 * (e-10))
},
indexGeo: (e) => {
const lat = Math.round(Math.abs(e.location.lat))
const lon = Math.round(Math.abs(e.location.lon))
return lat.toString() + lon.toString()
},
getApi: function () {
const promises = [];
this.cities.forEach(function(element){
const myUrl = apiUrl+element;
promises.push(axios.get(myUrl))
});
let self = this;
axios
.all(promises)
.then(axios.spread((...responses) => {
responses.forEach(res => self.items.push(res.data))
}))
.catch(error => console.log(error));
}
},
created() {
this.getApi()
this.show = true
}
}
The render when I update API :
By pushing to the existing array of items, you have to deal with the possibility of duplicates. This can be eliminated simply by replacing items every time the API call is made.
Replace:
responses.forEach(res => self.items.push(res.data))
with:
self.items = responses.map(res => res.data)
Forgive me, I'm new to normalizr+redux. I've managed to normalize my data and create a reducer and end up with :
state = {
installations:{
"1":{...},
"2":{...}
}
}
I would then like to filter this data for use in a UI component into two separate categories (in this case where the installation.operator is equal to the current user). I've managed an implementation that works however it seems exhaustive:
const mapStateToProps = (state, ownProps) => {
console.log("mapStateToProps", state.installations);
let assignedInstallations = Object.keys(state.installations)
.filter(i => {
return state.installations[i].operator == state.login;
})
.map(i => {
return state.installations[i];
});
let unassignedInstallations = Object.keys(state.installations)
.filter(i => {
return state.installations[i].operator != state.login;
})
.map(i => {
return state.installations[i];
});
return {
assignedInstallations,
unassignedInstallations,
loginUserId: state.login
};
};
I'm also new to ES6 and am not across all the new syntax shortcuts etc so I suspect there are much better ways to do this.
Is there a more succinct approach with a similar outcome?
you can do this with only one reduce():
const mapStateToProps = (state, ownProps) => {
console.log("mapStateToProps", state.installations);
let {assignedInstallations,
unassignedInstallations } = Object.keys(state.installations)
.reduce(function(acc, cur, i){
if(state.installations[i].operator == state.login){
acc.assignedInstallations.push(state.installations[i]);
}else{
acc.unassignedInstallations .push(state.installations[i]);
}
return acc
}, {assignedInstallations: [], unassignedInstallations: [] })
return {
assignedInstallations,
unassignedInstallations,
loginUserId: state.login
};
};
lodash (An utility library) have a notion of collection (Here is an example https://lodash.com/docs/4.17.4#filter for filter function). It takes as input Object or Array and returns an Array. It seems to fit to your needs. Here is the refactored code:
import {
filter,
} from 'lodash'
const mapStateToProps = (state, ownProps) => {
let assignedInstallations = filter(state.installations, installation => installation.operator == state.login);
let unassignedInstallations = filter(state.installations, installation => installation.operator != state.login);
return {
assignedInstallations,
unassignedInstallations,
loginUserId: state.login
};
};