In the product page now we’ll add the “Add to cart” button, which once clicked will add the product into the shopping cart.

In the src/views/SingleProduct.vue we add the button:

<button @click="addToCart">Add to cart</button>

which calls the addToCart method of the component when clicked:

methods: {
  addToCart() {
    this.$store.commit('addToCart', { product: this.product, quantity: 1 })
  }
}

The method calls the addToCart mutation on the Vuex store, passing the product object, and an integer that sets the quantity to be added to the cart.

We add a bit of styling:

button {
  margin-top: 50px;
  padding: 10px 40px;
  background-color: lightgreen;
  border-color: gray;
}

and we can move to the store file, src/store.js.

We add a new cart property to the state, which is an array:

state: {
  products: [],
  cart: []
}

And we add the addToCart mutation:

addToCart(state, newItem) {
  const index = state.cart.findIndex(
    itemInCart => itemInCart.product.slug === newItem.product.slug
  )

  if (index === -1) {
    //not existing
    state.cart = [...state.cart, newItem]
  } else {
    newItem.quantity += state.cart[index].quantity
    state.cart = state.cart
      .filter(item => item.product.slug !== newItem.product.slug)
      .concat(newItem)
  }
}

If the product is already existing in the cart, based on its slug, we increment its quantity. Otherwise, we add it as a new element.

We also add a cart getter, so we’ll later be able to access the cart content from any component:

getters: {
  products: state => state.products,
  cart: state => state.cart
}

When we add an item to the cart we can see it in the Vue DevTools, as part of the Vuex store, but there’s no visual indication in the page that anything happened.

We need a Cart component that should appear only if there is something in the cart. I decide to put it on every page, just below the navigation links.

So we create a src/components/Cart.vue file, and we include it in the src/App.vue component:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/products">Products</router-link> |
      <router-link to="/add-product">Add Product</router-link>
    </div>

    <Cart />

    <router-view/>
  </div>
</template>

<script>
import Cart from './components/Cart'

export default {
  name: 'AddProduct',
  components: {
    Cart
  }
}
</script>

Let’s dive into the Cart component code now.

We want to only show it when there is something in the cart so we can count the cart array length, using the Vuex store getter:

<template>
  <table v-if="$store.getters.cart.length > 0">
    ...
  </table>
</template>

then we create the table structure, and we iterate over the cart content, to show the details:

<template>
  <table v-if="$store.getters.cart.length > 0">
    <tr>
      <td colspan="3"><h2>Cart</h2></td>
    </tr>
    <tr>
      <th>Item</th>
      <th>Quantity</th>
      <th>Total Price</th>
    </tr>
    <tr v-for="(item, index) in $store.getters.cart" :key="index">
      <td>{{item.product.name}}</td>
      <td>{{item.quantity}}</td>
      <td>${{item.quantity * item.product.price}}</td>
    </tr>
  </table>
</template>

and we add a little styling to make it look good:

<style scoped>
tr,
th,
td {
  border: 1px solid #333;
  padding: 10px;
}
table {
  margin: 0 auto;
}

button {
  padding: 10px 40px;
  background-color: lightgreen;
  border-color: gray;
}
</style>

The current code is available at https://codesandbox.io/s/l2m012q5v7


Go to the next lesson