1. Set Up:
In your terminal, within your Vue 3 project:
//create project for day 03
vue create day2-sample
//Install vuex
npm install vuex@next
2. Create a Vuex Store:
Create a directory named store
in your src
directory, and within the store
directory, create a file named index.js
.
src/store/index.js:
import { createStore } from 'vuex';
const store = createStore({
state() {
return {
cart: []
};
},
mutations: {
addToCart(state, product) {
state.cart.push(product);
},
removeFromCart(state, productId) {
const index = state.cart.findIndex(p => p.id === productId);
if (index !== -1) {
state.cart.splice(index, 1);
}
}
},
actions: {
addProductToCart(context, product) {
context.commit('addToCart', product);
},
removeProductFromCart(context, productId) {
context.commit('removeFromCart', productId);
}
},
getters: {
cartProducts(state) {
return state.cart;
},
cartItemCount(state) {
return state.cart.length;
}
}
});
export default store;
3. Set Up Vuex in main.js:
src/main.js:
// ... other imports
import store from './store';
createApp(App).use(router).use(store).mount('#app');
4. Shopping Cart Component:
src/components/ShoppingCart.vue:
<template>
<div>
<h2>Your Shopping Cart</h2>
<ul v-if="cartProducts.length">
<li v-for="product in cartProducts" :key="product.id">
{{ product.name }} - {{ product.price }} x {{ product.quantity }}
<button @click="increaseQuantity(product.id)">+</button>
<button @click="decreaseQuantity(product.id)">-</button>
<button @click="removeFromCart(product.id)">Remove</button>
</li>
</ul>
<p v-else>No items in the cart.</p>
<p>Total Items: {{ cartItemCount }}</p>
<p>Total Price: {{ cartTotalPrice }}</p>
</div>
</template>
<script>
import { computed } from "vue";
import { useStore } from "vuex";
export default {
name: "ShoppingCart",
setup() {
const store = useStore();
const cartProducts = computed(() => store.getters.cartProducts);
const cartItemCount = computed(() => store.getters.cartItemCount);
const cartTotalPrice = computed(() => store.getters.cartTotalPrice);
const removeFromCart = (productId) => {
store.dispatch("removeProductFromCart", productId);
};
const increaseQuantity = (productId) => {
store.dispatch("increaseQuantity", productId);
};
const decreaseQuantity = (productId) => {
store.dispatch("decreaseQuantity", productId);
};
return {
cartProducts,
cartItemCount,
cartTotalPrice,
removeFromCart,
increaseQuantity,
decreaseQuantity,
};
},
};
</script>
<style scoped>
/* You can add any styling related to this component here */
</style>
5. Using the Store:
For this sample, let’s assume you have a Products
component where you list products and a button to add them to the cart.
src/components/ProductsItem.vue:
<template>
<div>
<h2>Available Products</h2>
<ul>
<li v-for="product in products" :key="product.id">
{{ product.name }} - {{ product.price }}
<button @click="addToCart(product)">Add to Cart</button>
</li>
</ul>
</div>
</template>
<script>
import { ref } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const products = ref([
{ id: 1, name: "Product A", price: "$10" },
{ id: 2, name: "Product B", price: "$20" },
{ id: 3, name: "Product c", price: "$20" },
{ id: 4, name: "Product d", price: "$30" },
{ id: 5, name: "Product e", price: "$40" },
// ... more products
]);
const addToCart = (product) => {
store.dispatch("addProductToCart", product);
console.log("Item");
};
return {
products,
addToCart,
};
},
};
</script>
Now, within your main app or any other layout component, you can include both the Products
and ShoppingCart
components. Users can add products to their cart and view the cart.
With this setup, you’ve created a global state (the cart) that any component can interact with. You can add more products, manage quantities, and introduce more advanced features like checking out or saving the cart.
The core principle here is that the state is centrally managed by Vuex, ensuring consistency and better management of application state.
6. Managing Product Quantity:
Instead of just adding products to the cart, we might want to handle quantities. Let’s modify our Vuex store and components to accommodate this.
import { createStore } from 'vuex';
function parsePrice(priceString) {
const price = parseFloat(priceString.replace("$", ""));
return isNaN(price) ? 0 : price; // Default to 0 if the price isn't valid
}
export default createStore({
state: {
cart: []
},
getters: {
cartProducts: state => state.cart,
cartItemCount: state => {
return state.cart.reduce((acc, product) => acc + product.quantity, 0);
},
cartTotalPrice: state => {
return state.cart.reduce((acc, product) => {
const productPrice = parsePrice(product.price); // Ensure price is a number, after parsing
const productQuantity = parseInt(product.quantity, 10) || 0; // Convert to int and default to 0 if NaN
return acc + productPrice * productQuantity;
}, 0);
}
},
mutations: {
addToCart(state, product) {
const productInCart = state.cart.find(p => p.id === product.id);
if (productInCart) {
productInCart.quantity++;
} else {
state.cart.push({ ...product, quantity: 1 });
}
},
incrementProductQuantity(state, productId) {
const product = state.cart.find(p => p.id === productId);
if (product) {
product.quantity = (product.quantity || 0) + 1;
}
},
decrementProductQuantity(state, productId) {
const product = state.cart.find(p => p.id === productId);
if (product && product.quantity > 0) {
product.quantity--;
}
},
removeProductFromCart(state, productId) {
state.cart = state.cart.filter(p => p.id !== productId);
}
},
actions: {
addProductToCart({ commit }, product) {
commit('addToCart', product);
},
increaseQuantity({ commit }, productId) {
commit('incrementProductQuantity', productId);
},
decreaseQuantity({ commit }, productId) {
commit('decrementProductQuantity', productId);
},
removeProductFromCart({ commit }, productId) {
commit('removeProductFromCart', productId);
}
}
});
7. Set Up Vue Router:
Create a directory named router
in your src
directory, and within the router
directory, create a file named index.js
.
import { createRouter, createWebHistory } from 'vue-router';
import ProductsItem from '../components/ProductsItem.vue';
import ShoppingCart from '../components/ShoppingCart.vue';
const routes = [
{ path: '/', redirect: '/products' },
{ path: '/products', component: ProductsItem },
{ path: '/cart', component: ShoppingCart }
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
8. Set Up Vue Router:
Configure Vue Router in main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store';
createApp(App).use(router).use(store).mount('#app');
9. Add Navigation:
Let’s add some basic navigation to easily navigate between the products page and the shopping cart.
src/App.vue (or wherever you define your main layout):
<template>
<div id="app">
<header>
<h1>{{ message }}</h1>
</header>
<nav>
<router-link to="/products">Products</router-link> |
<router-link to="/cart">Shopping Cart</router-link>
</nav>
<section>
<input
v-model="newItem"
@keyup.enter="addItem"
placeholder="Add a new item..."
/>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
<button @click="removeItem(item.id)">Remove</button>
</li>
</ul>
<div v-if="items.length === 0">No items added yet.</div>
</section>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
message: "Day 2 Vue Sample App Routers",
newItem: "",
items: [],
nextId: 1,
};
},
methods: {
addItem() {
if (this.newItem.trim() === "") return;
this.items.push({
id: this.nextId,
text: this.newItem.trim(),
});
this.newItem = "";
this.nextId++;
},
removeItem(id) {
this.items = this.items.filter((item) => item.id !== id);
},
},
};
</script>
<style>
/* Simple styling for better visualization */
header {
background-color: #4caf50;
color: white;
text-align: center;
padding: 1rem 0;
}
section {
max-width: 500px;
margin: 2rem auto;
border: 1px solid #ccc;
padding: 1rem;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
}
button {
background-color: #f44336;
color: white;
border: none;
cursor: pointer;
padding: 0.5rem 1rem;
border-radius: 5px;
}
button:hover {
background-color: #d32f2f;
}
nav a.router-link-exact-active {
font-weight: bold;
color: green;
}
</style>
Accessing the Components:
Now when you run your application:
- Accessing
/
will redirect you to/products
. - On
/products
, you will see the list of products. - On
/cart
, you will see the shopping cart and its items.
With Vue Router in place, you can easily manage routes, add route guards if necessary (like for authenticated routes), and more. This setup provides a foundation that can be expanded upon as your application grows.
See on github: https://github.com/LeoReyesDev/vue-tutorial/blob/day3-sample/