If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeOur cart page is nearly fully functional! Just one last task: bring the remove button to life!
Let's repeat the process we used for quantity. Start in cart-item. Find the button so we can add an "on click". I'll break this onto multiple lines. @click= then $emit and call the event, how about, removeFromCart, though remove-from-cart would better follow the standard naming convention for events.
| <template> | |
| <div :class="[$style.component, 'row', 'p-3']"> | |
| ... lines 3 - 29 | |
| <div class="col-3"> | |
| <button | |
| class="btn btn-info btn-sm" | |
| @click="$emit('removeFromCart')" | |
| > | |
| Remove | |
| </button> | |
| </div> | |
| </div> | |
| </template> | |
| ... lines 40 - 88 |
Unlike quantity, this time, we do not need to include any data with the event: we're simply saying "remove from cart".
Next, in index.vue, listen to this with @removeFromCart="". Do the same thing we did before: emit an event with the same name - removeFromCart - and make sure to include productId and colorId so that our parent component knows which item to remove. I'll copy these from the emit above.
| <template> | |
| <div> | |
| ... lines 3 - 6 | |
| <div v-if="items.length"> | |
| ... lines 8 - 20 | |
| <shopping-cart-item | |
| ... lines 22 - 29 | |
| @removeFromCart="$emit('removeFromCart', { | |
| productId: item.product['@id'], | |
| colorId: item.color ? item.color['@id'] : null, | |
| })" | |
| /> | |
| ... lines 35 - 38 | |
| </div> | |
| </div> | |
| </template> | |
| ... lines 42 - 72 |
Finally, we can listen to the removeFromCart event from the top level shopping-cart.vue. Scroll up. Hey! I have an extra import I can remove - yay! Keep going to find the <shopping-cart-list component. Add @removeFromCart="".
But this time, instead of calling a method on this component, let's immediately put a new method in the mixin to handle everything related to removing an item from the cart.
Over in get-shopping-cart.js, add a new method called removeProductFromCart(). We know this will need the productId and colorId to identify which item it is. Inside, we can call another function from cart-service that we haven't used yet. It's called removeItemFromCart(). Hit tab so that it adds the import for us. Pass this the cart: this.cart, productId and colorId.
| import { | |
| ... lines 2 - 4 | |
| removeItemFromCart, | |
| ... line 6 | |
| } from '@/services/cart-service'; | |
| export default { | |
| ... lines 10 - 19 | |
| methods: { | |
| ... lines 21 - 45 | |
| async removeProductFromCart(productId, colorId) { | |
| await removeItemFromCart(this.cart, productId, colorId); | |
| ... lines 48 - 49 | |
| }, | |
| ... lines 51 - 55 | |
| }, | |
| }; |
Hold Command or Ctrl and click removeItemFromCart to jump into that function. Just like with updateCartItemQuantity(), this does two things: it modifies the cart object to remove the item and then makes an AJAX call to save that to the server.
| ... lines 1 - 89 | |
| /** | |
| * Removes an item from the shopping cart | |
| * | |
| * @param {CartCollection} cart | |
| * @param {string} productId | |
| * @param {string} colorId | |
| * @return {Promise} | |
| */ | |
| export async function removeItemFromCart(cart, productId, colorId) { | |
| cart.items = cart.items.filter( | |
| (item) => (!(item.product === productId && item.color === colorId)), | |
| ); | |
| const response = await axios.put(getCartIri(), cart); | |
| return { items: response.data.items }; | |
| } | |
| ... lines 107 - 149 |
Back in the mixin, don't forget to update the header: this.updateCartHeaderTotal(). Oh, but let's await for the AJAX call to finish before doing this.
| ... lines 1 - 8 | |
| export default { | |
| ... lines 10 - 19 | |
| methods: { | |
| ... lines 21 - 45 | |
| async removeProductFromCart(productId, colorId) { | |
| await removeItemFromCart(this.cart, productId, colorId); | |
| this.updateCartHeaderTotal(); | |
| }, | |
| ... lines 51 - 55 | |
| }, | |
| }; |
Ok: let's put our new method to work! Back in shopping-cart.vue, since we use that mixin, we can call the method directly: removeProductFromCart() passing $event.productId and $event.colorId.
| <template> | |
| <div :class="[$style.component, 'container-fluid']"> | |
| <div class="row"> | |
| ... lines 4 - 5 | |
| <div class="col-xs-12 col-lg-9"> | |
| ... lines 7 - 8 | |
| <div class="content p-3"> | |
| ... lines 10 - 11 | |
| <shopping-cart-list | |
| ... lines 13 - 15 | |
| @removeFromCart="removeProductFromCart( | |
| $event.productId, | |
| $event.colorId, | |
| )" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| ... lines 26 - 103 |
Testing time! Find your browser and do a full page refresh to be safe. Right now, we have 15 items. Remove the couch with quantity 3 and.... it worked! The header says 12, the item is gone, the total updated. We rock! And, when we refresh, the item is still gone.
Next: I have a challenge for us! To help sell, I mean, "share", more high quality merchandise with the world, the marketing department has asked us to add a "featured product" to the sidebar of the cart page with the ability to add that item to the cart directly from this page - including choosing the color.
To accomplish this, we're going need to reuse a lot of code between the sidebar and the cart show page. Let's do it!
"Houston: no signs of life"
Start the conversation!
// 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
}
}