DEV Community

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

Posted on • Edited on

9 3

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.

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more