Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

More Mixin Logic: Updating Quantity in Header

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

In cart-item.vue, when the quantity changes, we're emitting an updateQuantity event. But... it occurred to me that we're passing too much data in the event... or really, we're passing more data than we need to.

... lines 1 - 37
<script>
... lines 39 - 40
export default {
name: 'ShoppingCartItem',
... lines 43 - 56
methods: {
updateQuantity(event) {
this.$emit('updateQuantity', {
... lines 60 - 62
});
},
},
};
</script>
... lines 68 - 87

Passing Less Data to the Event

Think about it: both productId and colorId come from this.item... which is passed to us as a prop... which means that our parent component already knows the productId and colorId that this component instance is tied to. Passing these back to our parent in the event data is... redundant!

... lines 1 - 37
<script>
... lines 39 - 40
export default {
name: 'ShoppingCartItem',
... lines 43 - 56
methods: {
updateQuantity(event) {
this.$emit('updateQuantity', {
productId: this.item.product['@id'],
colorId: this.item.color ? this.item.color['@id'] : null,
quantity: parseFloat(event.target.value),
});
},
},
};
</script>
... lines 68 - 87

On a philosophical level, all we should need to pass to updateQuantity is the new quantity.

This is a tiny detail, but let's clean this up. Copy both productId and colorId and then delete them from the event data.

... lines 1 - 37
<script>
... lines 39 - 40
export default {
name: 'ShoppingCartItem',
... lines 43 - 56
methods: {
updateQuantity(event) {
this.$emit('updateQuantity', {
quantity: parseFloat(event.target.value),
});
},
},
};
</script>
... lines 66 - 85

Now go to index.vue. When we emit the event here, we do need to include productId and colorId so that ShoppingCart knows which item's quantity is being updated. We can add that info right here: call $emit() and this time pass an object. Paste those keys - productId and colorId - change this.item to item... and then make sure to continue including quantity set to $event.quantity.

<template>
<div>
... lines 3 - 6
<div v-if="items.length">
... lines 8 - 20
<shopping-cart-item
... lines 22 - 24
@updateQuantity="$emit('updateQuantity', {
productId: item.product['@id'],
colorId: item.color ? item.color['@id'] : null,
quantity: $event.quantity,
})"
/>
... lines 31 - 34
</div>
</div>
</template>
... lines 38 - 68

That's it! It worked fine before... and it works fine now... but this feels better.

Fixing the Cart Header

The one thing that is not working when we change the quantity is the cart header: it's still not updating. But we do have logic to handle this. Open shopping-cart.vue and find the updateQuantity() method.

We know that this component uses a mixin: get-shopping-cart.js. And that holds the code to update the shopping cart count in the header. We run this after adding a new product to the cart.

Ok then: if we isolated this into its own method, we could call that from inside of shopping-cart.vue when the quantity is updated. Let's do it! But wait, let's go even further. We have this nice mixin... whose job is to hold cart data and methods related to that data. So it makes sense to also include a method here to handle everything related to updating an item's quantity.

Adding More Logic to the Mixin

Check it out: in the mixin, add a new method called updateProductQuantity() with 3 arguments for the 3 pieces of info this needs: productId, colorId and quantity.

... lines 1 - 7
export default {
... lines 9 - 18
methods: {
... lines 20 - 39
updateProductQuantity(productId, colorId, quantity) {
... line 41
},
},
};

Now go to shopping-cart, copy the updateCartItemQuantity() function that updates the quantity and makes the AJAX call, and paste it here. When I did that, PhpStorm automatically added the import on top for me... though I don't love that syntax.

import {
... lines 2 - 4
updateCartItemQuantity,
} from '@/services/cart-service';
export default {
... lines 9 - 18
methods: {
... lines 20 - 39
updateProductQuantity(productId, colorId, quantity) {
updateCartItemQuantity(this.cart, productId, colorId, quantity);
},
},
};

Anyways, now that we have this method, we can call it from our shopping-cart component. Remove the console.log() and just say this.updateProductQuantity() passing the same arguments, except that we don't need to pass this.cart: the mixin already has that.

... lines 1 - 22
<script>
... lines 24 - 30
export default {
name: 'ShoppingCart',
... lines 33 - 75
methods: {
... lines 77 - 82
updateQuantity({ productId, colorId, quantity }) {
this.updateProductQuantity(productId, colorId, quantity);
},
},
};
</script>
... lines 89 - 99

Sweet! Just a little bit of reorganization so that more cart-related logic lives in the mixin. And if we try it... yup! It's not broken. We're awesome!

Thanks to this, updating the cart header will be even easier. Start by copying the header logic and scrolling down so we can create a new method. Call it updateCartHeaderTotal() and paste!

... lines 1 - 7
export default {
... lines 9 - 44
updateCartHeaderTotal() {
document.getElementById('js-shopping-cart-items')
.innerHTML = getCartTotalItems(this.cart).toString();
},
},
};

Now, very simply, at the end of addProductToCart() call this.updateCartHeaderTotal() and... repeat that in updateProductQuantity: this.updateCartHeaderTotal().

Oh, and we don't need to do this, but I'm going to add an await and then make the method async. This will now wait for that quantity AJAX call to finish and then update the quantity in the header.

... lines 1 - 7
export default {
... lines 9 - 18
methods: {
async addProductToCart(product, selectedColorId, quantity) {
... lines 21 - 35
this.updateCartHeaderTotal();
},
... line 38
async updateProductQuantity(productId, colorId, quantity) {
await updateCartItemQuantity(this.cart, productId, colorId, quantity);
this.updateCartHeaderTotal();
},
... lines 44 - 48
},
};

Let's try it! I'll do a full page refresh to be safe. The cart header says 13. Increase a quantity... boom! Everything updates - including the cart header.

Next: let's hook up the last part of the cart functionality: the remove from cart button.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

This course is also built to work with Vue 3!

What JavaScript libraries does this tutorial use?

// package.json
{
    "devDependencies": {
        "@fortawesome/fontawesome-free": "^5.15.1", // 5.15.1
        "@symfony/webpack-encore": "^0.30.0", // 0.30.2
        "axios": "^0.19.2", // 0.19.2
        "bootstrap": "^4.4.1", // 4.5.3
        "core-js": "^3.0.0", // 3.6.5
        "eslint": "^6.7.2", // 6.8.0
        "eslint-config-airbnb-base": "^14.0.0", // 14.2.0
        "eslint-plugin-import": "^2.19.1", // 2.22.1
        "eslint-plugin-vue": "^6.0.1", // 6.2.2
        "regenerator-runtime": "^0.13.2", // 0.13.7
        "sass": "^1.29.0", // 1.29.0
        "sass-loader": "^8.0.0", // 8.0.2
        "vue": "^2.6.11", // 2.6.12
        "vue-loader": "^15.9.1", // 15.9.4
        "vue-template-compiler": "^2.6.11", // 2.6.12
        "webpack-notifier": "^1.6.0" // 1.8.0
    }
}
userVoice