Build a Shopping Cart in NodeJS and Vue Vite

In this article we are going to build shopping cart frontend for our application. We will be using Vue Vite which has a faster hot module reload.

You can check our backend part built in Nodejs, which we already have published.

We need to create our application directory for Vite. Create a vue-cart directory in your desktop and run setup Vite by running this command:

cd desktop
mkdir vue-cart && cd vue-cart
npm init vite-app vue-cart

After initializing a Vite application, run this on your terminal:

cd vue-cart
npm install
code . && npm run dev

The code . command will open up the project in visual studio code.

We will continue by setting up our user interface for the application. You can get all our UI components from  WrapPixel’s UI Kit.

WrapPixel is an online template store where you could get great UI templates and vue templates.

Let’s create two components: product.vue and chart.vue. The product.Vue will list all our products and cart.vue will lists all items in our cart.

We need to configure Bootstrap into our application by adding the CDN into the index.html file.

We will only be interested in the bootstrap css CDN, so head over to the official bootstrap CDN and copy the CSS link and add it into the index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="icon" href="/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Vite app</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
    integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
  <div id="app"></div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

With this we can now setup our component. We will start by creating our root Vuejs file and setup routing for our application.

Lets start by installing the Vue 3 router by running:

npm i --save vue-router@v4.0.0-alpha.11

After installing this we need to setup our routes for our application. We will have a basic routes to show our cart items.

Create a router.js file inside the src directory and add the following codes:

import {
    createWebHistory,
    createRouter
} from "vue-router";
import Home from "./components/HelloWorld.vue";
import Product from './components/product.vue'
import Cart from './components/cart.vue'
const history = createWebHistory();
const routes = [{
    path: "/",
    component: Product
}, {
    path: "/cart",
    component: Cart
}, ];
const router = createRouter({
    history,
    routes
});
export default router;

Here, What we are basically doing is register our cart and product components as routes.

We have to register our routes in our main.js file:

import {
    createApp
} from 'vue'
import App from './App.vue'
import './index.css'
import router from "./router";
createApp(App).use(router).mount('#app')

With this done, we have to change our App.vue root component to this so that it will render all our components:

<template>
  <nav class="navbar navbar-expand-lg navbar-light bg-info">
    <div class="container">
      <router-link to="/" class="navbar-brand">Vue Cart </router-link>
      <button
        class="navbar-toggler"
        type="button"
        data-toggle="collapse"
        data-target="#navbarNav"
        aria-controls="navbarNav"
        aria-expanded="false"
        aria-label="Toggle navigation"
      >
         
      </button>
      <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
        <ul class="navbar-nav">
          <li class="nav-item active">
            <router-link to="/" class="nav-link">Home </router-link>
          </li>
          <li class="nav-item">
            <router-link to="/cart" class="nav-link">Cart </router-link>
          </li>
        </ul>
      </div>
    </div>
  </nav>
  <router-view />
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
  name: "App",
  components: {
    HelloWorld,
  },
};
</script>

We can now setup our products card and make http request to our backend API.

Lets create a simple user interface for our home page. Add this to the product.vue file:

<template>
  <main>
    <section>
      <div
        class="banner-innerpage"
        style="
          background-image: url(https://images.pexels.com/photos/1005638/pexels-photo-1005638.jpeg?cs=srgb&dl=pexels-oleg-magni-1005638.jpg&fm=jpg);
        "
      >
        <div class="container">
          <!-- Row  -->
          <div class="row justify-content-center">
            <!-- Column -->
            <div
              class="col-md-6 align-self-center text-center"
              data-aos="fade-down"
              data-aos-duration="1200"
            >
              <h1 class="title">Shop listing</h1>
              <h6 class="subtitle op-8">
                We are small team of creative people working together
              </h6>
            </div>
            <!-- Column -->
          </div>
        </div>
      </div>
    </section>
    <section>
      <div class="spacer">
        <div class="container">
          <div class="row mt-5">
            <div class="col-lg-9">
              <div class="row shop-listing">
                <div class="col-lg-6">
                  <div class="card shop-hover border-0">
                    <img
                      src="https://images.pexels.com/photos/1005638/pexels-photo-1005638.jpeg?cs=srgb&dl=pexels-oleg-magni-1005638.jpg&fm=jpg"
                      alt="wrapkit"
                      class="img-fluid"
                    />
                    <div class="card-img-overlay align-items-center">
                      <button class="btn btn-md btn-info">
                        Add to cart
                      </button>
                    </div>
                  </div>
                  <div class="card border-0">
                    <h6><a href="#" class="link">Mens Wear </a></h6>
                    <h6 class="subtitle">by Wisdom</h6>
                    <h5 class="font-medium m-b-30">
                      $195 / <del class="text-muted line-through">$225</del>
                    </h5>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  </main>
</template>
<style>
.banner-innerpage {
  padding: 150px 0 100px;
  background-size: cover;
  background-position: center center;
}
.banner-innerpage .title {
  color: #ffffff;
  text-transform: uppercase;
  font-weight: 700;
  font-size: 40px;
  line-height: 40px;
}
.banner-innerpage .subtitle {
  color: #ffffff;
}
.shop-listing .shop-hover {
  position: relative;
}
.shop-listing .shop-hover .card-img-overlay {
  display: none;
  background: rgba(255, 255, 255, 0.5);
  -webkit-box-pack: center;
  -webkit-justify-content: center;
  -ms-flex-pack: center;
  justify-content: center;
}
.shop-listing .shop-hover:hover .card-img-overlay {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
.shop-listing .shop-hover .label {
  padding: 5px 10px;
  position: absolute;
  top: 10px;
  right: 10px;
}
/*******************
shop table
*******************/
.shop-table td {
  padding: 30px 0;
}
</style>

This will create a simple user interface for our application. Up next we need to start consuming our endpoints. Start up the backend API that we created in our previous tutorial, after doing that we can now use javascript fetch to make request to the backend,

Add the following script to the <script> section in the product component:

<script>
export default {
  data() {
    return {
      products: [],
    };
  },
  created() {
    this.getProducts();
  },
  methods: {
    async getProducts() {
      const res = await fetch("http://localhost:4000/product");
      const data = await res.json();
      this.products = new Proxy(data.data, {});
      console.log(this.products);
    },
  },
};
</script>

This will make a request to our backend API and get the list of all products and store the products in the products array defined in

the data instance.

We can now loop through our products using the Vuejs v-for directive by modifying the column that holds the product card to this:

<div
          class="col-lg-4"
          v-for="product in products"
          :key="product._id"
          >
          <div class="card shop-hover border-0">
          <img
          :src="'http://localhost:4000/' + product.image"
          alt="wrapkit"
          class="img-fluid"
          />
            <div class="card-img-overlay align-items-center">
             <button class="btn btn-md btn-info">
                Add to Cart
             </button>
            </div>
          </div>
          <div class="card border-0">
             <h6>
                <a href="#" class="link">{{ product.name }} </a>
             </h6>
             <h6 class="subtitle">by Wisdom</h6>
             <h5 class="font-medium m-b-30">
                $195 / <del class="text-muted line-through">$225</del>
             </h5>
          </div>
     </div>

This will list all the products stored in our database

Image for post

Now lets add the add to cart feature. We will create a method which will take the product Id and a default quantity as one and then send it to our backend.

Lets define our addToCart methods:

async addToCart(id, quantity) {
      try {
        const response = await fetch("http://localhost:4000/cart", {
          method: "POST",
          body: JSON.stringify({
            productId: id,
            quantity: quantity,
          }),
          headers: {
            "Content-type": "application/json; charset=UTF-8",
          },
        });
        let data = await response.json();
        alert("Item added to cart");
        console.log(data);
      } catch (err) {
        alert("Something went wrong");
        console.log(err);
      }
    },

After doing this we have to add the click even to our button to trigger the method:

<button
    class="btn btn-md btn-info"
    @click="addToCart(product._id, 1)"
    >
      Add to Cart
 </button>

Here we pass the productid and the default quantity as 1.

We can now start working on the viewing of cart items, incrementing of cart ttems and emptying of cart.

Lets head over to our cart.vue component and build our user interface:

<template>
  <main>
    <section>
      <div
        class="banner-innerpage"
        style="
          background-image: url(https://images.pexels.com/photos/1005638/pexels-photo-1005638.jpeg?cs=srgb&dl=pexels-oleg-magni-1005638.jpg&fm=jpg);
        "
      >
        <div class="container">
          <!-- Row  -->
          <div class="row justify-content-center">
            <!-- Column -->
            <div
              class="col-md-6 align-self-center text-center"
              data-aos="fade-down"
              data-aos-duration="1200"
            >
              <h1 class="title">Cart</h1>
              <h6 class="subtitle op-8">
                We are small team of creative people working together.
              </h6>
            </div>
            <!-- Column -->
          </div>
        </div>
      </div>
    </section>
    <section>
      <div class="spacer">
        <div class="container">
          <div class="row mt-5">
            <div class="col-lg-9">
              <div class="row shop-listing">
                <table class="table shop-table">
                  <tr>
                    <th class="b-0">Image</th>
                    <th class="b-0">Name</th>
                    <th class="b-0">Price</th>
                    <th class="b-0">Quantity</th>
                    <th class="b-0 text-right">Total Price</th>
                  </tr>
                  <tr>
                    <td>
                      <img
                        src="../assets/images/innerpage/shop/1.jpg"
                        width="200"
                        alt="wrapkit"
                      />
                    </td>
                    <td>
                      Mens Wear
                    </td>
                    <td>
                      $3000
                    </td>
                    <td>
                      <button class="btn btn-primary btn-sm">+</button> 3
                      <button class="btn btn-primary btn-sm">
                        -
                      </button>
                    </td>
                    <td class="text-right">
                      <h5 class="font-medium m-b-30">
                        $195
                      </h5>
                    </td>
                  </tr>
                  <tr>
                    <td colspan="4" align="right">Subtotal :$1000</td>
<td colspan="4" align="right">
                      <button class="btn btn-danger">Empty Cart</button>
                    </td>
                  </tr>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  </main>
</template>

This will give us this simple user interface:

Image for post

Let’s implement getting of all cart items by creating a method to get all the item from our backend:

<script>
export default {
  data() {
    return {
      carts: {},
    };
  },
  methods: {
    async getCartItems() {
      try {
        const res = await fetch("http://localhost:4000/cart");
        const data = await res.json();
        this.carts = new Proxy(data.data, {});
        console.log(this.carts);
      } catch (err) {
        console.log(err);
      }
    },
  },
  created() {
    this.getCartItems();
  },
};
</script>

We can now loop through our cart items by modifying our table to this:

<table class="table shop-table">
                      <tr>
                        <th class="b-0">Name</th>
                        <th class="b-0">Price</th>
                        <th class="b-0">Quantity</th>
                        <th class="b-0 text-right">Total Price</th>
                      </tr>
                      <tr v-for="(item, id) in carts.items" :key="id">
                        <td>{{ item.productId.name }}</td>
                        <td>{{ item.productId.price }}</td>
                        <td>
                          <button
                            class="btn btn-primary btn-sm"
                            @click="increaseQty(item.productId._id)"
                          >+</button>
                          {{ item.quantity }}
                          <button
                            class="btn btn-primary btn-sm"
                          >-</button>
                        </td>
                        <td class="text-right">
                          <h5 class="font-medium m-b-30">{{ item.total }}</h5>
                        </td>
                      </tr>
                      <tr>
                        <td colspan="3" align="right">Subtotal :{{ carts.subTotal }}</td>
                        <td colspan="4" align="right">
                          <button class="btn btn-danger">Empty Cart</button>
                        </td>
                      </tr>
                    </table>

We can now implement the increment in cart item quantity by adding a method to increment it:

async increaseQty(id) {
      try {
        const res = await fetch("http://localhost:4000/cart", {
          method: "POST",
          body: JSON.stringify({
            productId: id,
            quantity: 1,
          }),
          headers: {
            "Content-type": "application/json; charset=UTF-8",
          },
        });
        this.getCartItems();
        alert("Item Increamented");
      } catch (err) {
        console.log(err);
      }
    },

And then add a click even to listen to this method:

<button
   class="btn btn-primary btn-sm"                          
   @click="increaseQty(item.productId._id)"
   >+
</button>

Clicking on the + button will increment the quantity of the items and also update the prices.

Lets implement the empty cart feature. This will empty our cart and then redirect us to the product listing page. Create a method to do this:

async emptyCart() {
      try {
        const res = await fetch("http://localhost:4000/cart/empty-cart", {
          method: "DELETE",
        });
        const data = await res.json();
        this.$router.push({
          path: "/",
        });
      } catch (err) {
        console.log(err);
      }
    },

Then we add an event listener to listen to this method:

<button class="btn btn-danger" @click="emptyCart">Empty cart</button>

Exercise

  • Implement the decrement feature
  • Implement remove product from cart

After implementing this, push your work to git and add the link in the comment section. Lets have some Fun

Originally published on Medium.com

#vue #vuejs #nodejs #web-development #javascript

Build a Shopping Cart in NodeJS and Vue Vite
11.00 GEEK