Shopping Cart Using Vue js 3 Composition API and Pinia Part 1
In today's tutorial, we are going to create a shopping cart using Vue js 3 composition API and pinia, pinia is the new state management library recommended by vue team.
Packages we need
I assume that you have already created a Vue application, all the packages you need are in the JSON file below.
{
"name": "vuejs-shopping-cart",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"bootstrap": "^5.2.0-beta1",
"bootstrap-icons": "^1.8.2",
"pinia": "^2.0.14",
"sweetalert2": "^11.4.14",
"vue": "^3.2.25",
"vue-router": "^4.0.15"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.3.3",
"vite": "^2.9.9"
}
}
Add Bootstrap 5 & Bootstrap 5 Icons & Vue Router & Pinia to our project
Inside the src folder, we update the main.js file we add the routes, pinia, and the bootstrap 5 icons & styles.
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import router from './router'
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min.js'
import 'bootstrap-icons/font/bootstrap-icons.css'
import 'sweetalert2/dist/sweetalert2.min.css';
createApp(App)
.use(createPinia())
.use(router)
.mount('#app')
Create store
Our project structure will be like the below:
> src > components > router > store
Inside the store folder, we add a new file index.js which contains the pinia store with getters, actions, and a global state.
import { defineStore } from 'pinia'
import Swal from 'sweetalert2'
export const useShoppingStore = defineStore('shopping', {
state: () => {
return {
products: [
{
id: 1,
name: 'Iphone 12',
price: 700,
image: 'https://cdn.pixabay.com/photo/2016/11/20/08/33/camera-1842202__480.jpg'
},
{
id: 2,
name: 'Samsung s10',
price: 400,
image: 'https://cdn.pixabay.com/photo/2016/03/27/19/43/samsung-1283938__340.jpg'
},
{
id: 3,
name: 'Samsung Tv',
price: 1200,
image: 'https://cdn.pixabay.com/photo/2019/06/30/18/19/tv-4308538__480.jpg'
},
{
id: 4,
name: 'Huwawei Mate',
price: 900,
image: 'https://cdn.pixabay.com/photo/2017/08/11/14/19/honor-2631271__340.jpg'
}
],
cartItems : []
}
},
getters: {
countCartItems(){
return this.cartItems.length;
},
getCartItems(){
return this.cartItems;
}
},
actions: {
addToCart(item) {
let index = this.cartItems.findIndex(product => product.id === item.id);
if(index !== -1) {
this.products[index].quantity += 1;
Swal.fire({
position: 'top-end',
icon: 'success',
title: 'Your item has been updated',
showConfirmButton: false,
timer: 1500
});
}else {
item.quantity = 1;
this.cartItems.push(item);
Swal.fire({
position: 'top-end',
icon: 'success',
title: 'Your item has been saved',
showConfirmButton: false,
timer: 1500
});
}
},
incrementQ(item) {
let index = this.cartItems.findIndex(product => product.id === item.id);
if(index !== -1) {
this.cartItems[index].quantity += 1;
Swal.fire({
position: 'top-end',
icon: 'success',
title: 'Your item has been updated',
showConfirmButton: false,
timer: 1500
});
}
},
decrementQ(item) {
let index = this.cartItems.findIndex(product => product.id === item.id);
if(index !== -1) {
this.cartItems[index].quantity -= 1;
if(this.cartItems[index].quantity === 0){
this.cartItems = this.cartItems.filter(product => product.id !== item.id);
}
Swal.fire({
position: 'top-end',
icon: 'success',
title: 'Your item has been updated',
showConfirmButton: false,
timer: 1500
});
}
},
removeFromCart(item) {
this.cartItems = this.cartItems.filter(product => product.id !== item.id);
Swal.fire({
position: 'top-end',
icon: 'success',
title: 'Your item has been removed',
showConfirmButton: false,
timer: 1500
});
}
},
})
Create routes
Next in the router folder, we add a new file index.js which contains the routes.
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../components/Home.vue'
import Cart from '../components/Cart.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/cart',
name: 'cart',
component: Cart
},
]
});
export default router
Create the Header component
Inside components, we add a new file Header.vue.
<template>
<nav class="navbar navbar-expand-lg rounded navbar-dark bg-dark">
<div class="container-fluid">
<router-link class="navbar-brand" to="/">Vuejs3 Shopping Cart</router-link>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<router-link class="nav-link" aria-current="page" to="/">Home</router-link>
</li>
<li class="nav-item">
<router-link class="nav-link" to="/cart"><i class="bi bi-cart-check"></i> ({{data.countCartItems}})</router-link>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script setup>
import { useShoppingStore } from '../stores'
//get store
const data = useShoppingStore();
</script>
<style>
</style>