DEV Community

Cover image for How to build a Shopping Cart App with Vue.js [ Series - Portfolio Apps ]
Sith Norvang
Sith Norvang

Posted on • Updated on

How to build a Shopping Cart App with Vue.js [ Series - Portfolio Apps ]

This is the second episode of "Portfolio Apps" series. Today, we are going to build a simple shopping cart. I choose simulating a little shop about smartphones and tablets.

Let's dive in this new tutorial.

1.0 / Setup

2.0 / Components & Router

[ 1.1 ] Install Vue 3

# Install latest stable of Vue

yarn global add @vue/cli
Enter fullscreen mode Exit fullscreen mode

[ 1.2 ] Creating a new project

This time, let's manually select features for this new Vue application.

# run this command

vue create shopping-cart-app
Enter fullscreen mode Exit fullscreen mode

vue create

Press your spacebar to select "Router" and "Vuex"

router & vuex

Choose this options :

  • version 3 of Vue.js "3.x (Preview)", vue 3 versioning
  • the "history mode for router", history mode
  • "ESLint with error prevention only", ES Lint
  • "Lint On Save",
  • "Package .json"
  • Preset "Yes"
  • Custom and record the name you wish for this template.

[ 1.3 ] Images

Create four folders "iPadPro", "iPhone12Pro", "iPhoneSE" and "S21".

assets
|-- iPadPro
|-- iPhone12Pro
|-- iPHoneSE
|-- S21
Enter fullscreen mode Exit fullscreen mode

Search and import all pictures in each folder as following :

import all pictures

[ 1.4 ] Vuex

Let's prepare all Vuex files. In "index.js", we need to initialize four states :

  • cart,
  • total,
  • qty,
  • products,
# ../store/index.js

import { createStore } from "vuex";

import rootMutations from "./mutations.js";
import rootActions from "./actions.js";
import rootGetters from "./getters.js";

const store = createStore({
  state() {
    return {
      cart: [],
      total: 0,
      qty: 0,
      products: [
        {
          id: 1,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Graphite",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
          price: 999,
        },
        {
          id: 2,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Silver",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
          price: 999,
        },
        {
          id: 3,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Gold",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
          price: 999,
        },
        {
          id: 4,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Pacific Blue",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
          price: 999,
        },
        {
          id: 5,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Graphite",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
          price: 1199.0,
        },
        {
          id: 6,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Silver",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
          price: 1199,
        },
        {
          id: 7,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Gold",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
          price: 1199,
        },
        {
          id: 8,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Pacific Blue",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
          price: 1199,
        },
        {
          id: 9,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Graphite",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGraphite.jpg"),
          price: 1399,
        },
        {
          id: 10,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProSilver.jpg"),
          price: 1399,
        },
        {
          id: 11,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Gold",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProGold.jpg"),
          price: 1399,
        },
        {
          id: 12,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone 12 Pro",
          color: "Pacific Blue",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPhone12Pro/iPhone12ProPacificBlue.jpg"),
          price: 1399,
        },
        {
          id: 13,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "64 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 399,
        },
        {
          id: 14,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "64 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 399,
        },
        {
          id: 15,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "Red",
          capacity: "64 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
          price: 399,
        },
        {
          id: 14,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 499,
        },
        {
          id: 15,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 499,
        },
        {
          id: 16,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "Red",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
          price: 499,
        },
        {
          id: 17,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 599,
        },
        {
          id: 18,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "White",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSEWhite.jpg"),
          price: 599,
        },
        {
          id: 19,
          type: "Smartphone",
          brand: "Apple",
          model: "iPhone SE",
          color: "Red",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPhoneSE/iPhoneSERed.jpg"),
          price: 599,
        },
        {
          id: 20,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Space Gray",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1299,
        },
        {
          id: 21,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Silver",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1299,
        },
        {
          id: 22,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Space Gray",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1499,
        },
        {
          id: 23,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Silver",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1499,
        },
        {
          id: 24,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1599,
        },
        {
          id: 25,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1599,
        },
        {
          id: 26,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Space Gray",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1699,
        },
        {
          id: 27,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Silver",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1699,
        },
        {
          id: 28,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Space Gray",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 29,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi",
          color: "Silver",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1799,
        },
        {
          id: 30,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1546,
        },
        {
          id: 31,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1546,
        },
        {
          id: 32,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1599,
        },
        {
          id: 33,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1599,
        },
        {
          id: 34,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1699,
        },
        {
          id: 35,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1699,
        },
        {
          id: 36,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 37,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1799,
        },
        {
          id: 38,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1899,
        },
        {
          id: 39,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1899,
        },
        {
          id: 40,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Space Gray",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1999,
        },
        {
          id: 41,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi",
          color: "Silver",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1999,
        },
        {
          id: 42,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 43,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 44,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1899,
        },
        {
          id: 45,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1899,
        },
        {
          id: 46,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 47,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 48,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1699,
        },
        {
          id: 49,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1699,
        },
        {
          id: 50,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1799,
        },
        {
          id: 51,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1799,
        },
        {
          id: 52,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1899,
        },
        {
          id: 53,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 11-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1899,
        },
        {
          id: 54,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 1999,
        },
        {
          id: 55,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "128 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 1999,
        },
        {
          id: 56,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 2099,
        },
        {
          id: 55,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "256 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 2099,
        },
        {
          id: 56,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 2199,
        },
        {
          id: 57,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "512 GB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 2199,
        },
        {
          id: 58,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 2299,
        },
        {
          id: 59,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "1 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 2299,
        },
        {
          id: 60,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Space Gray",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSpaceGray.jpg"),
          price: 2399,
        },
        {
          id: 61,
          type: "Tablet",
          brand: "Apple",
          model: "iPad Pro 12.9-inch display Wifi + Cellular",
          color: "Silver",
          capacity: "2 TB",
          imgSrc: require("@/assets/iPadPro/iPadProSilver.jpg"),
          price: 2399,
        },
        {
          id: 62,
          type: "Smartphone",
          brand: "Samsung",
          model: "S21 5G",
          color: "Phantom Grey",
          capacity: "128 GB",
          imgSrc: require("@/assets/S21/S21PhantomGrey.jpg"),
          price: 799,
        },
        {
          id: 63,
          type: "Smartphone",
          brand: "Samsung",
          model: "S21 5G",
          color: "Phantom Pink",
          capacity: "128 GB",
          imgSrc: require("@/assets/S21/S21PhantomPink.jpg"),
          price: 799,
        },
        {
          id: 64,
          type: "Smartphone",
          brand: "Samsung",
          model: "S21 5G",
          color: "Phantom Violet",
          capacity: "128 GB",
          imgSrc: require("@/assets/S21/S21PhantomViolet.jpg"),
          price: 799,
        },
        {
          id: 65,
          type: "Smartphone",
          brand: "Samsung",
          model: "S21 5G",
          color: "Phantom White",
          capacity: "128 GB",
          imgSrc: require("@/assets/S21/S21PhantomWhite.jpg"),
          price: 799,
        },
      ],
    };
  },
  mutations: rootMutations,
  actions: rootActions,
  getters: rootGetters,
});

export default store;

Enter fullscreen mode Exit fullscreen mode

Now, we are going to create three files "actions.js", "mutations.js", "getters.js"

store
|-- actions.js
|-- getters.js
|-- index.js
|-- mutations.js

Enter fullscreen mode Exit fullscreen mode

This application required two actions :

  • "addToCart"
  • "removeFromCart"

And two mutations :

  • "addProductToCart"
  • "removeProductFromCart"
# ./store/actions.js

export default {
  addToCart(context, payload) {
    const prodId = payload.id;
    const products = context.rootGetters.products;
    const product = products.find((prod) => prod.id === prodId);
    context.commit("addProductToCart", product);
  },
  removeFromCart(context, payload) {
    context.commit("removeProductFromCart", payload);
  },
};

Enter fullscreen mode Exit fullscreen mode

And two mutations :

  • "addProductToCart"
  • "removeProductFromCart"
# ./store/mutations.js

export default {
  addProductToCart(state, payload) {
    const productData = payload;
    const productInCartIndex = state.cart.findIndex(
      (ci) => ci.id === productData.id
    );
    if (productInCartIndex >= 0) {
      state.cart[productInCartIndex].qty++;
    } else {
      const newItem = {
        id: productData.id,
        type: productData.type,
        brand: productData.brand,
        model: productData.model,
        color: productData.color,
        capacity: productData.capacity,
        imgSrc: productData.imgSrc,
        price: productData.price,
        qty: 1,
      };
      state.cart.push(newItem);
    }
    state.qty++;
    state.total += productData.price;
  },

  removeProductFromCart(state, payload) {
    const prodId = payload.id;
    const productInCartIndex = state.cart.findIndex(
      (cartItem) => cartItem.id === prodId
    );
    const prodData = state.cart[productInCartIndex];
    state.cart.splice(productInCartIndex, 1);
    state.qty -= prodData.qty;
    state.total -= prodData.price * prodData.qty;
  },
};

Enter fullscreen mode Exit fullscreen mode

And four getters which return each state :

# ./store/getters

export default {
  products(state) {
    return state.products;
  },
  totalSum(state) {
    return state.total;
  },
  quantity(state) {
    return state.qty;
  },
  cart(state) {
    return state.cart;
  }
};

Enter fullscreen mode Exit fullscreen mode

[ 2.1 ] Views & Components

We are going to create four views :

views
|-- AllProduts.vue
|   # Display all products
|-- Smartphones.vue
|   # Filter only Smartphones Products
|-- Tablets.vue
|   # Filter only Tablets Products
|-- Cart.vue
    # Display all products added to Cart

Enter fullscreen mode Exit fullscreen mode

About the first view "All Products", we need to create three components :

AllProducts.vue
|-- Wrapper.vue
|-- ProductCard.vue

Enter fullscreen mode Exit fullscreen mode
# ../views/AllProducts.vue

<template>
  <Wrapper>
    <ProductCard
      v-for="product in products"
      :key="product.id"
      :id="product.id"
      :type="product.type"
      :brand="product.brand"
      :model="product.model"
      :color="product.color"
      :capacity="product.capacity"
      :imgSrc="product.imgSrc"
      :price="product.price"
    />
  </Wrapper>
</template>

<script>
import ProductCard from "@/components/ProductCard";

export default {
  components: {
    ProductCard,
  },
  computed: {
    products() {
      const productsSort = this.$store.getters["products"];
      return productsSort.sort((a, b) => {
        if (a.model < b.model) {
          return -1;
        }
        if (a.model > b.model) {
          return 1;
        }
        return 0;
      });
    },
  },
};
</script>

Enter fullscreen mode Exit fullscreen mode
# ../components/Wrapper.vue

<template>
  <div class="wrapper">
    <div class="product-container">
      <slot />
    </div>
  </div>
</template>

<script>
export default {
  name: "Wrapper",
}
</script>

<style>
.wrapper {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.product-container {
  padding-top: 170px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  flex-direction: row;
}
</style>

Enter fullscreen mode Exit fullscreen mode

<template>
  <div class="product-card">
    <div class="image-container">
      <img class="img-style" :src="imgSrc" :alt="brand + model" />
    </div>
    <p class="price-label">₿ {{ price.toFixed(2) }}</p>
    <button @click="addToCart">Add to Cart</button>
    <span class="product-title">
      {{ brand }} {{ model }} {{ color }} {{ capacity }}
    </span>
  </div>
</template>

<script>
export default {
  name: "ProductCard",
  props: [
    "id",
    "type",
    "brand",
    "model",
    "color",
    "capacity",
    "imgSrc",
    "price",
  ],
  methods: {
    addToCart() {
      this.$store.dispatch("addToCart", {
        id: this.id,
        type: this.type,
        brand: this.brand,
        model: this.model,
        color: this.color,
        capacity: this.capacity,
        imgSrc: this.imgSrc,
        price: this.price,
      });
    },
  },
};
</script>

<style scoped>
.product-card {
  display: flex;
  flex-direction: column;
  height: 450px;
  max-height: 450px;
  max-width: 250px;
  background-color: white;
  border-radius: 15px;
  flex: 1 1 240px;
  margin: 10px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(60, 60, 60, 0.2);
  padding: 30px;
}

.img-style {
  display: flex;
  height: 250px;
  object-fit: cover;
}

.image-container {
  display: flex;
  justify-content: center;
}

.product-title {
  display: flex;
  color: rgba(66, 185, 131, 1);
  font-weight: bold;
  height: 100%;
  align-items: flex-start;
  justify-content: center;
}

.price-label {
  font-weight: bold;
  font-size: 20px;
}
</style>

Enter fullscreen mode Exit fullscreen mode

Why should we create components in two different folders ? Because "ProductCard.vue" and "Wrapper.vue" are reusable components ! We are going to use it again 💪 in "Smartphones.vue" and "Tablets.vue" :

# ../views/Smartphones.vue

<template>
  <Wrapper>
    <ProductCard
      v-for="product in products"
      :key="product.id"
      :id="product.id"
      :type="product.type"
      :brand="product.brand"
      :model="product.model"
      :color="product.color"
      :capacity="product.capacity"
      :imgSrc="product.imgSrc"
      :price="product.price"
    />
  </Wrapper>
</template>

<script>
import ProductCard from "@/components/ProductCard";

export default {
  components: {
    ProductCard,
  },
  computed: {
    products() {
      const productsFilter = this.$store.getters["products"];
      return productsFilter.filter((product) => {
        if (product.type.includes("Smartphone")) {
          return true;
        } else {
          return false;
        }
      });
    },
  },
};
</script>

Enter fullscreen mode Exit fullscreen mode
# ../views/Tablets.vue 

<template>
  <Wrapper>
    <ProductCard
      v-for="product in products"
      :key="product.id"
      :id="product.id"
      :type="product.type"
      :brand="product.brand"
      :model="product.model"
      :color="product.color"
      :capacity="product.capacity"
      :imgSrc="product.imgSrc"
      :price="product.price"
    />
  </Wrapper>
</template>

<script>
import ProductCard from "@/components/ProductCard";

export default {
  components: {
    ProductCard,
  },
  computed: {
    products() {
      const productsFilter = this.$store.getters["products"];
      return productsFilter.filter((product) => {
        if (product.type.includes("Tablet")) {
          return true;
        } else {
          return false;
        }
      });
    },
  },
};
</script>

Enter fullscreen mode Exit fullscreen mode

This is how reusable components works ! Awesome right ? 👍

# ../views/Cart.vue

<template>
  <Wrapper>
    <div class="container-cart">
      <h2>Your Cart</h2>
      <h3>Total Amount: ₿ {{ cartTotal }}</h3>
      <ul>
        <CartCard
          v-for="product in cartProducts"
          :key="product.id"
          :id="product.id"
          :type="product.type"
          :brand="product.brand"
          :model="product.model"
          :color="product.color"
          :capacity="product.capacity"
          :imgSrc="product.imgSrc"
          :price="product.price"
          :qty="product.qty"
        ></CartCard>
      </ul>
    </div>
  </Wrapper>
</template>

<script>
import CartCard from "@/components/CartCard.vue";

export default {
  components: {
    CartCard,
  },
  computed: {
    cartTotal() {
      return this.$store.getters["totalSum"].toFixed(2);
    },
    cartProducts() {
      return this.$store.getters["cart"];
    },
  },
};
</script>

<style scoped>
.container-cart {
  display: flex;
  flex-direction: column;
}

h2 {
  color: #292929;
  text-align: center;
  border-bottom: 2px solid #ccc;
  padding-bottom: 1rem;
}

h3 {
  text-align: center;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
</style>
Enter fullscreen mode Exit fullscreen mode
# ../components/CartCard.vue

<template>
  <li>
    <div class="image-container">
      <img :src="imgSrc" :alt="model" />
    </div>
    <div>
      <h3>{{ brand }} {{ model }} {{ color }} {{ capacity }}</h3>
      <div class="item-data">
        <div>
          Price per Item:
          <strong>₿ {{ price.toFixed(2) }}</strong>
        </div>
        <div>
          Quantity:
          <strong>{{ qty }}</strong>
        </div>
      </div>
      <div class="item-total">Total: ₿ {{ itemTotal }}</div>
      <button @click="remove">Remove</button>
    </div>
  </li>
</template>

<script>
export default {
  props: [
    "id",
    "type",
    "brand",
    "model",
    "color",
    "capacity",
    "imgSrc",
    "price",
    "qty",
  ],
  computed: {
    itemTotal() {
      return (this.price * this.qty).toFixed(2);
    },
  },
  methods: {
    remove() {
      this.$store.dispatch("removeFromCart", { id: this.id });
    },
  },
};
</script>

<style scoped>
.image-container {
  display: flex;
  align-items: center;
  justify-content: center;
}

li {
  margin: 1rem auto;
  padding: 1rem;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  text-align: center;
  max-width: 25rem;
  border-radius: 15px;
  border: 1px solid rgba(60, 60, 60, 0.2);
}

img {
  display: flex;
  height: 250px;
  object-fit: cover;
}

.item-data {
  display: flex;
  justify-content: space-between;
}

.item-total {
  font-weight: bold;
  margin: 1rem 0;
  border-top: 1px solid #474747;
  border-bottom: 2px solid #474747;
  padding: 0.25rem 0;
  width: auto;
}
</style>

Enter fullscreen mode Exit fullscreen mode
# ../components/TheHeader.vue

<template>
  <div id="nav">
    <router-link to="/">All Products</router-link>
    <router-link to="/smartphones">Smartphones</router-link>
    <router-link to="/tablets">Tablets</router-link>
    <router-link to="/cart">Cart</router-link>
    <span class="counter-cart" v-if="socketCart > 0"> {{ socketCart }} </span>
  </div>
</template>

<script>

export default {
  computed: {
    socketCart() {
      return this.$store.getters["quantity"];
    },
  },
};
</script>

<style>
#nav {
  position: fixed;
  background: rgb(255, 255, 255);
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, 1) 94%,
    rgba(255, 255, 255, 0) 100%
  );
  width: 100%;
  padding: 30px;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
  text-decoration: none;
  line-height: 30px;
  padding: 0px 30px 0px 30px;
}

#nav a.router-link-exact-active {
  color: #42b983;
  text-decoration: none;
  padding: 0px 30px 0px 30px;
  line-height: 30px;
}

.counter-cart {
  display: inline-block;
  padding: 0.3rem 0.7rem 0.3rem 0.7rem;
  background-color: rgba(162, 205, 2, 1);
  color: white;
  border-radius: 50px;
}
</style>

Enter fullscreen mode Exit fullscreen mode
# ../App.vue

<template>
  <TheHeader />
  <router-view />
</template>

<script>
import TheHeader from "./components/TheHeader.vue";

export default {
  components: {
    TheHeader,
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

body {
  margin: 0;
}

button {
  font: inherit;
  cursor: pointer;
  background-color: rgba(66, 185, 131, 1);
  color: white;
  border: 1px solid rgba(66, 185, 131, 1);
  padding: 0.5rem 1.5rem;
  border-radius: 30px;
  margin-bottom: 20px;
}

button:hover,
button:active {
  background-color: #82dcb1;
  border-color: #82dcb1;
}
</style>

Enter fullscreen mode Exit fullscreen mode

[ 2.2 ] Router

Every components are ready now. We can configuring the router.

# ../router/index.js

import { createRouter, createWebHistory } from "vue-router";
import AllProducts from "../views/AllProducts.vue";

const routes = [
  {
    path: "/",
    name: "AllProducts",
    component: AllProducts,
  },
  {
    path: "/smartphones",
    name: "Smartphones",
    component: () => import("../views/Smartphones.vue"),
  },
  {
    path: "/tablets",
    name: "Tablets",
    component: () => import("../views/Tablets.vue"),
  },
  {
    path: "/cart",
    name: "cart",
    component: () => import("../views/Cart.vue"),
  },
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});

export default router;

Enter fullscreen mode Exit fullscreen mode
# ../main.js

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store/index.js";

import Wrapper from "@/components/Wrapper";

const app = createApp(App);

app.use(router);
app.use(store);

app.component("Wrapper", Wrapper);

app.mount("#app");

Enter fullscreen mode Exit fullscreen mode

Ready ?

Done ! You can try this Shopping Cart Application here :

https://shopping-cart-demo-sith.netlify.app

See you soon in next episode of "Portfolio Apps" series.

Discussion (0)