The Following Baby Can Not Buy, Please "Buy Now" Under the Order, or Re-add the Shopping Cart Order
The author selected the Open up Source Initiative to receive a donation every bit part of the Write for DOnations program.
Introduction
Vue.js is a performant and progressive Javascript framework. It is a popular framework on GitHub and has an active and helpful community.
In order to evidence the capabilities of the Vue web framework, this tutorial will lead you lot through building the shopping cart of an e-commerce app. This app will store product information and concord the products that the customer wants to purchase for checkout later. To shop the information, you will explore a widely used state management library for Vue.js: Vuex. This will permit the shopping cart awarding to persist data to a server. You will too handle asynchronous task management using Vuex.
One time you end the tutorial, yous will have a functioning shopping cart application similar the following:
Prerequisites
-
Y'all will need a development environment running Node.js; this tutorial was tested on Node.js version 10.22.0 and npm version 6.xiv.6. To install this on macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Evolution Environment on macOS or the Installing Using a PPA section of How To Install Node.js on Ubuntu xviii.04.
-
You will as well need a bones knowledge of JavaScript, HTML, and CSS, which you can detect in our How To Build a Website With HTML serial, How To Build a Website With CSS serial, and in How To Lawmaking in JavaScript.
Step 1 — Setting Upwards the Application with Vue CLI
Every bit of version 4.5.0, Vue CLI now provides a built-in pick to choose the Vue 3 preset when creating a new project. The latest version of Vue CLI allows y'all to use Vue three out of the box and to update your existing Vue 2 projection to Vue 3. In this step, you will use the Vue CLI to make your project, then install the front-stop dependencies.
First, install the latest version of Vue CLI by executing the post-obit control from the concluding:
- npm install -g @vue/cli
This will install Vue CLI globally on your system.
Notation: On some systems, installing an npm package globally can upshot in a permission fault, which volition interrupt the installation. Since it is a security all-time practice to avert using sudo with npm install, you lot can instead resolve this by changing npm'south default directory. If you encounter an EACCES error, follow the instructions at the official npm documentation.
Check you lot have the right version with this control:
- vue --version
You will get output like the post-obit:
Output
@vue/cli iv.5.x Note: If you already accept the older version of Vue CLI installed globally, execute the following command from the final to upgrade:
- npm update -g @vue/cli
At present, you can create a new project:
- vue create vuex-shopping-cart
This uses the Vue CLI control vue create to brand a projection named vuex-shopping-cart. For more information on the Vue CLI, check out How To Generate a Vue.js Unmarried Page App With the Vue CLI.
Next, y'all will receive the post-obit prompt:
Output
Vue CLI v4.5.ten ? Please pick a preset: (Use pointer keys) ❯ Default ([Vue two] babel, eslint) Default (Vue 3 Preview) ([Vue iii] boom-boom, eslint) Manually select features Cull the Manually select features option from this list.
Next, y'all volition encounter the post-obit prompt to customize your Vue app:
Output
... ◉ Choose Vue version ◯ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◉ Router ◉ Vuex ◯ CSS Pre-processors ◯ Linter / Formatter ❯◯ Unit Testing ◯ E2E Testing From this list, select Choose Vue version, Router, and Vuex. This will permit yous to cull your version of Vue and use Vuex and Vue Router.
Next, choose three.x (Preview) for your version of Vue, respond no (North) to history mode, and select the selection to have your configurations In dedicated config file. Finally, answer N to avoid saving the setup for a time to come project.
At this point, Vue will create your application.
Afterwards the project creation, movement into the folder using the command:
- cd vuex-shopping-cart
To commencement, you'll install Bulma, a free, open-source CSS framework based on Flexbox. Add together Bulma to your project by running the following command:
- npm install bulma
To utilize Bulma CSS in your projection, open up your app's entry signal, the main.js file:
- nano src/master.js
So add the following highlighted import line:
vuex-shopping-cart/src/master.js
import { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' import './../node_modules/bulma/css/bulma.css' createApp (App) . employ (shop) . utilize (router) . mount ( '#app' ) Salvage and close the file.
In this app, you'll utilise the Axios module to brand requests to your server. Add the Axios module by running the following command:
- npm install axios
Now, run the app to make sure it is working:
- npm run serve
Navigate to http://localhost:8080 in your browser of choice. You lot will find the Vue app welcome page:
Once you have confirmed that Vue is working, end your server with CTRL+C.
In this footstep, yous globally installed Vue CLI in your computer, created a Vue project, installed the required npm packages Axios and Bulma, and imported Bulma to the project in the main.js file. Next, you will fix a dorsum-end API to store data for your app.
Step 2 — Setting Upward the Backend
In this step, you will create a separate backend to work with your Vue project. This will exist in a different projection folder from your front-end Vue application.
Outset, move out of your Vue directory:
- cd ..
Make a separate directory named cart-backend:
- mkdir cart-backend
In one case you lot have your back-cease folder, brand it your working directory:
- cd cart-backend
You will become started by initializing the project with the necessary file. Create the file structure of your app with the following commands:
- touch server.js
- touch server-cart-information.json
- touch server-product-information.json
You utilise the bear upon command hither to create empty files. The server.js file will hold your Node.js server, and the JSON will concord data for the shop's products and the user's shopping cart.
Now run the following control to create a packet.json file:
- npm init
For more information on npm and Node, check out our How To Lawmaking in Node.js series.
Install these dorsum-end dependencies into your Node projection:
- npm install meantime limited trunk-parser
Express is a Node framework for spider web applications, which will provide useful abstractions for handling API requests. Concurrently volition be used to run the Express back-end server and the Vue.js development server simulteneously. Finally, body-parser is an Express middleware that will parse requests to your API.
Side by side, open up a server.js file in the root of your awarding:
- nano server.js
Then add together the following code:
cart-backend/server.js
const express = crave ( 'express' ) ; const bodyParser = require ( 'body-parser' ) ; const fs = require ( 'fs' ) ; const path = require ( 'path' ) ; const app = limited ( ) ; const PRODUCT_DATA_FILE = path. join (__dirname, 'server-product-information.json' ) ; const CART_DATA_FILE = path. join (__dirname, 'server-cart-data.json' ) ; app. set ( 'port' , (process.env. PORT || 3000 ) ) ; app. use (bodyParser. json ( ) ) ; app. utilise (bodyParser. urlencoded ( { extended : true } ) ) ; app. utilise ( ( req, res, next ) => { res. setHeader ( 'Cache-Control' , 'no-enshroud, no-shop, must-revalidate' ) ; res. setHeader ( 'Pragma' , 'no-cache' ) ; res. setHeader ( 'Expires' , '0' ) ; side by side ( ) ; } ) ; app. listen (app. get ( 'port' ) , ( ) => { console. log ( ` Find the server at: http://localhost: ${app. go ( 'port' ) } / ` ) ; } ) ; This snippet commencement adds the Node modules to your backend, including the fs module to write to your filesystem and the path module to make defining filepaths easier. Yous and then initialize the Express app and save references to your JSON files as PRODUCT_DATA_FILE and CART_DATA_FILE. These volition be used equally data repositories. Finally, you lot created an Limited server, set the port, created a middleware to prepare the response headers, and gear up the server to listen on your port. For more information on Limited, see the official Limited documentation.
The setHeader method sets the header of the HTTP responses. In this case, you are using Cache-Control to directly the caching of your app. For more information on this, check out the Mozilla Developer Network article on Cache-Command.
Next, you lot will create an API endpoint that your frontend will query to add together an item to the shopping cart. To do this, you volition use app.postal service to listen for an HTTP Mail request.
Add the following lawmaking to server.js just after the terminal app.use() middleware:
cart-backend/server.js
... app. use ( ( req, res, next ) => { res. setHeader ( 'Cache-Command' , 'no-cache, no-store, must-revalidate' ) ; res. setHeader ( 'Pragma' , 'no-cache' ) ; res. setHeader ( 'Expires' , '0' ) ; side by side ( ) ; } ) ; app. post ( '/cart' , ( req, res ) => { fs. readFile ( CART_DATA_FILE , ( err, data ) => { const cartProducts = JSON . parse (data) ; const newCartProduct = { id : req.body.id, title : req.body.title, description : req.body.clarification, price : req.trunk.price, image_tag : req.body.image_tag, quantity : 1 } ; permit cartProductExists = imitation ; cartProducts. map ( ( cartProduct ) => { if (cartProduct.id === newCartProduct.id) { cartProduct.quantity++ ; cartProductExists = true ; } } ) ; if ( !cartProductExists) cartProducts. push button (newCartProduct) ; fs. writeFile ( CART_DATA_FILE , JSON . stringify (cartProducts, nix , 4 ) , ( ) => { res. setHeader ( 'Enshroud-Control' , 'no-cache' ) ; res. json (cartProducts) ; } ) ; } ) ; } ) ; app. listen (app. become ( 'port' ) , ( ) => { console. log ( ` Notice the server at: http://localhost: ${app. go ( 'port' ) } / ` ) ; } ) ; This code receives the request object containing the cart items from the frontend and stores them in the server-cart-data.json file in the root of your projection. Products hither are JavaScript objects with id, title, description, price, image_tag, and quantity properties. The code besides checks if the cart already exists to ensure that requests for a repeated production merely increase the quantity.
At present, add together code to create an API endpoint to remove an particular from the shopping cart. This time, you volition use app.delete to listen for an HTTP DELETE asking.
Add the following code to server.js just later the previous endpoint:
cart-backend/server.js
... fs. writeFile ( CART_DATA_FILE , JSON . stringify (cartProducts, null , iv ) , ( ) => { res. setHeader ( 'Cache-Control' , 'no-cache' ) ; res. json (cartProducts) ; } ) ; } ) ; } ) ; app. delete ( '/cart/delete' , ( req, res ) => { fs. readFile ( CART_DATA_FILE , ( err, information ) => { let cartProducts = JSON . parse (data) ; cartProducts. map ( ( cartProduct ) => { if (cartProduct.id === req.body.id && cartProduct.quantity > 1 ) { cartProduct.quantity-- ; } else if (cartProduct.id === req.torso.id && cartProduct.quantity === 1 ) { const cartIndexToRemove = cartProducts. findIndex ( cartProduct => cartProduct.id === req.torso.id) ; cartProducts. splice (cartIndexToRemove, ane ) ; } } ) ; fs. writeFile ( CART_DATA_FILE , JSON . stringify (cartProducts, zip , four ) , ( ) => { res. setHeader ( 'Cache-Control' , 'no-cache' ) ; res. json (cartProducts) ; } ) ; } ) ; } ) ; app. listen (app. get ( 'port' ) , ( ) => { console. log ( ` Find the server at: http://localhost: ${app. go ( 'port' ) } / ` ) ; // eslint-disable-line no-console } ) ; This lawmaking receives the request object containing the item to exist removed from the cart and checks the server-cart-data.json file for this detail via its id. If it exists and the quantity is greater than one, then the quantity of the item in the cart is deducted. Otherwise, if the item's quantity is less than 1, it will exist removed from the cart and the remaining items will be stored in the server-cart-data.json file.
To give your user additional functionality, you lot tin can now create an API endpoint to remove all items from the shopping cart. This volition also listen for a DELETE request.
Add the following highlighted lawmaking to server.js after the previous endpoint:
cart-backend/server.js
... fs. writeFile ( CART_DATA_FILE , JSON . stringify (cartProducts, null , four ) , ( ) => { res. setHeader ( 'Cache-Control' , 'no-cache' ) ; res. json (cartProducts) ; } ) ; } ) ; } ) ; app. delete ( '/cart/delete/all' , ( req, res ) => { fs. readFile ( CART_DATA_FILE , ( ) => { let emptyCart = [ ] ; fs. writeFile ( CART_DATA_FILE , JSON . stringify (emptyCart, null , 4 ) , ( ) => { res. json (emptyCart) ; } ) ; } ) ; } ) ; app. listen (app. get ( 'port' ) , ( ) => { console. log ( ` Detect the server at: http://localhost: ${app. get ( 'port' ) } / ` ) ; // eslint-disable-line no-console } ) ; This code is responsible for removing all the items from the cart by returning an empty array.
Next, you volition create an API endpoint to retrieve all the products from the product storage. This will use app.get to listen for a Go request.
Add together the following code to server.js afterwards the previous endpoint:
cart-backend/server.js
... app. delete ( '/cart/delete/all' , ( req, res ) => { fs. readFile ( CART_DATA_FILE , ( ) => { let emptyCart = [ ] ; fs. writeFile ( CART_DATA_FILE , JSON . stringify (emptyCart, nothing , iv ) , ( ) => { res. json (emptyCart) ; } ) ; } ) ; } ) ; app. become ( '/products' , ( req, res ) => { fs. readFile ( PRODUCT_DATA_FILE , ( err, information ) => { res. setHeader ( 'Cache-Control' , 'no-cache' ) ; res. json ( JSON . parse (data) ) ; } ) ; } ) ; ... This lawmaking uses the file organisation's native readFile method to fetch all the information in the server-product-data.json file and returns them in JSON format.
Finally, you will create an API endpoint to remember all the items from the cart storage:
cart-backend/server.js
... app. get ( '/products' , ( req, res ) => { fs. readFile ( PRODUCT_DATA_FILE , ( err, data ) => { res. setHeader ( 'Enshroud-Control' , 'no-cache' ) ; res. json ( JSON . parse (data) ) ; } ) ; } ) ; app. get ( '/cart' , ( req, res ) => { fs. readFile ( CART_DATA_FILE , ( err, information ) => { res. setHeader ( 'Cache-Control' , 'no-cache' ) ; res. json ( JSON . parse (data) ) ; } ) ; } ) ; ... Similarly, this code uses the file organisation's native readFile method to fetch all the information in the server-cart-data.json file and returns them in JSON format.
Save and close the server.js file.
Next, you will add some mock data to your JSON files for testing purposes.
Open up the server-cart-data.json file you lot created before:
- nano server-cart-data.json
Add the following array of production objects:
cart-backend/server-cart-data.json
[ { "id" : ii , "championship" : "MIKANO Engine" , "clarification" : "Lorem ipsum dolor sit down amet, consectetur dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis! " , "toll" : 650.9 , "image_tag" : "diesel-engine.png" , "quantity" : i } , { "id" : 3 , "title" : "SEFANG Engine" , "description" : "Lorem ipsum dolor sit amet, consectetur dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!" , "toll" : 619.9 , "image_tag" : "sefang-engine.png" , "quantity" : 1 } ] This shows two engines that will start out in the user'due south shopping cart.
Relieve and shut the file.
Now open the server-product-information.json file:
- nano server-production-data.json
Add the following data in server-product-data.json file:
cart-backend/server-product-data.json
[ { "id" : 1 , "title" : "Cat Engine" , "description" : "Lorem ipsum dolor sit amet, consectetur dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit down inventore aliquam sunt quam quis!" , "product_type" : "ability ready/diesel engine" , "image_tag" : "True cat-engine.png" , "created_at" : 2020 , "owner" : "Colton" , "owner_photo" : "prototype-colton.jpg" , "email" : "colt@gmail.com" , "price" : 719.9 } , { "id" : 2 , "title" : "MIKANO Engine" , "description" : "Lorem ipsum dolor sit amet, consectetur dignissimos suscipit voluptatibus distinctio, error nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis! " , "product_type" : "power ready/diesel fuel engine" , "image_tag" : "diesel fuel-engine.png" , "created_at" : 2020 , "owner" : "Colton" , "owner_photo" : "image-colton.jpg" , "e-mail" : "filly@gmail.com" , "price" : 650.9 } , { "id" : 3 , "title" : "SEFANG Engine" , "description" : "Lorem ipsum dolor sit amet, consectetur dignissimos suscipit voluptatibus distinctio, fault nostrum expedita omnis ipsum sit down inventore aliquam sunt quam quis!" , "product_type" : "power prepare/diesel fuel engine" , "image_tag" : "sefang-engine.png" , "created_at" : 2017 , "owner" : "Anne" , "owner_photo" : "image-anne.jpg" , "email" : "anne@gmail.com" , "price" : 619.9 } , { "id" : four , "championship" : "CAT Engine" , "clarification" : "Lorem ipsum dolor sit amet, consectetur dignissimos suscipit voluptatibus distinctio, fault nostrum expedita omnis ipsum sit inventore aliquam sunt quam quis!" , "product_type" : "power prepare/diesel engine" , "image_tag" : "lawn-mower.png" , "created_at" : 2017 , "owner" : "Irene" , "owner_photo" : "image-irene.jpg" , "email" : "irene@gmail.com" , "toll" : 319.nine } ] This volition hold all the possible products that the user can put in their cart.
Save and close the file.
Finally, execute this command to run the server:
- node server
Yous volition receive something like this on your concluding:
Output
Find the server at: http://localhost:3000/ Go out this server running in this window.
Finally, you volition set a proxy server in your Vue app. This will enable the connection betwixt the frontend and backend.
Go to the root directory of your Vue app:
- cd ../vuex-shopping-cart
In the terminal, run this command to create a Vue configuration file:
- nano vue.config.js
And so, add this code:
vuex-shopping-cart/vue.config.js
module.exports = { devServer : { proxy : { '/api' : { target : 'http://localhost:3000/' , changeOrigin : true , pathRewrite : { '^/api' : '' } } } } } This will send requests from your frontend to your dorsum-end server at http://localhost:3000/. For more than data on proxy configuration, review the Vue devServer.proxy documentation.
Save and close the file.
In this step, y'all wrote server-side code that will handle API endpoints for your shopping cart. You started by creating the file construction and ended with adding necessary lawmaking in the server.js file and data in your JSON files. Next, y'all will fix the land storage for your frontend.
Step 3 — Setting Up State Management with Vuex
In Vuex, the store is where the country of the awarding is kept. The application state can but be updated by dispatching actions within a component that volition then trigger mutations in the store. The Vuex store is made up of the land, mutations, actions, and getters.
In this step, yous're going to build each of these pieces, later on which you will couple everything together into a Vuex store.
State
Now you will create a place to store state for your application.
The shop folder in the root directory src of your project is automatically created at the time of the project setup. Locate the shop binder in the src directory of your project and then create a new folder named modules:
- mkdir src/shop/modules
Inside this folder, create the production and cart folders:
- mkdir src/store/modules/product
- mkdir src/shop/modules/cart
These volition agree all the state files for your product inventory and your user'south cart. You will build these two files up at the aforementioned time, each open in a separate terminal. This way, you lot will be able to compare your mutations, getters, and deportment side-by-side.
Finally, open up an index.js file in the product folder:
- nano src/store/modules/production/index.js
Add the post-obit lawmaking to create a land object containing your productItems:
vuex-shopping-cart/src/shop/modules/product/index.js
import axios from 'axios' ; const state = { productItems : [ ] } Save the file and keep information technology open.
Similarly, in a new terminal, add together an index.js file to the cart directory with the following:
- nano src/store/modules/cart/index.js
Then add code for the cartItems:
vuex-shopping-cart/src/store/modules/cart/index.js
import axios from 'axios' ; const land = { cartItems : [ ] } Save this file, but proceed it open up.
In these lawmaking snippets, you imported the Axios module and set the state. The state is a store object that holds the application-level information that needs to be shared betwixt components.
At present that you've set usa, head over to mutations.
Mutations
Mutations are methods that change the store country. They usually consist of a cord blazon and a handler that accepts the state and payload every bit parameters.
You will now create all the mutations for your application.
Add together the following code in the production/index.js file just later on the country section:
vuex-shopping-cart/src/store/modules/product/index.js
... const mutations = { UPDATE_PRODUCT_ITEMS ( state, payload ) { state.productItems = payload; } } This creates a mutations object that holds an UPDATE_PRODUCT_ITEMS method that sets the productItems array to the payload value.
Similarly, add the post-obit code in the cart/alphabetize.js file just after the state department:
vuex-shopping-cart/src/store/modules/cart/index.js
... const mutations = { UPDATE_CART_ITEMS ( state, payload ) { state.cartItems = payload; } } This creates a similar UPDATE_CART_ITEMS for your user'southward shopping cart. Note that this follows the Flux architecture fashion of making references to mutations in capital letters.
Actions
Actions are methods that volition handle mutations, then that mutations are insulated from the rest of your application code.
In product/index.js, create an actions object with all the deportment for your application:
vuex-shopping-cart/src/store/modules/product/alphabetize.js
... const deportment = { getProductItems ( { commit } ) { axios. get ( ` /api/products ` ) . and then ( ( response ) => { commit ( 'UPDATE_PRODUCT_ITEMS' , response.data) } ) ; } } Hither the getProductItems method sends an asynchronous GET request to the server using the Axios package that you installed earlier. When the request is successful, the UPDATE_PRODUCT_ITEMS mutation is called with the response information as the payload.
Side by side, add the following deportment object to cart/index.js:
vuex-shopping-cart/src/store/modules/cart/alphabetize.js
... const actions = { getCartItems ( { commit } ) { axios. get ( '/api/cart' ) . and so ( ( response ) => { commit ( 'UPDATE_CART_ITEMS' , response.data) } ) ; } , addCartItem ( { commit } , cartItem ) { axios. post ( '/api/cart' , cartItem) . then ( ( response ) => { commit ( 'UPDATE_CART_ITEMS' , response.data) } ) ; } , removeCartItem ( { commit } , cartItem ) { axios. delete ( '/api/cart/delete' , cartItem) . then ( ( response ) => { commit ( 'UPDATE_CART_ITEMS' , response.data) } ) ; } , removeAllCartItems ( { commit } ) { axios. delete ( '/api/cart/delete/all' ) . then ( ( response ) => { commit ( 'UPDATE_CART_ITEMS' , response.data) } ) ; } } In this file, you create the getCartItems method, which sends an asynchronous GET request to the server. When the request is successful, the UPDATE_CART_ITEMS mutation is called with the response data equally the payload. The same happens with the removeAllCartItems method, although it makes a DELETE request to the server. The removeCartItem and addCartItem methods receives the cartItem object as a parameter for making a DELETE or POST request. Later a successful request, the UPDATE_CART_ITEMS mutation is called with the response data as the payload.
You used ES6 destructuring to decouple the commit method from the Vuex context object. This is similar to using context.commit.
Getters
Getters are to an application store what computed properties are to a component. They return computed information from shop land methods that involve receiving computed state data.
Next, create a getters object to go all the information for the production module:
vuex-shopping-cart/src/store/modules/product/alphabetize.js
... const getters = { productItems : state => state.productItems, productItemById : ( state ) => ( id ) => { return country.productItems. detect ( productItem => productItem.id === id) } } Hither, you lot fabricated a method productItems that returns the list of product items in the state, followed past productItemById, a higher order function that returns a unmarried product by its id.
Adjacent, create a getters object in cart/index.js:
vuex-shopping-cart/src/shop/modules/cart/index.js
... const getters = { cartItems : state => country.cartItems, cartTotal : state => { return country.cartItems. reduce ( ( acc, cartItem ) => { return (cartItem.quantity * cartItem.cost) + acc; } , 0 ) . toFixed ( 2 ) ; } , cartQuantity : state => { return country.cartItems. reduce ( ( acc, cartItem ) => { return cartItem.quantity + acc; } , 0 ) ; } } In this snippet, yous fabricated the cartItems method, which returns the list of cart items in the state, followed past cartTotal, which returns the computed value of the full amount of cart items bachelor for checkout. Finally, you fabricated the cartQuantity method, which retuns the quantity of items in the cart.
Exporting the Module
The final part of the product and cart modules will export the country, mutations, actions, and getters objects and so that other parts of the awarding can access them.
In product/index.js, add the following code at the stop of the file:
vuex-shopping-cart/src/shop/modules/product/alphabetize.js
... const productModule = { state, mutations, actions, getters } consign default productModule; This collects all your state objects into the productModule object, and then exports information technology equally a module.
Save production/index.js and shut the file.
Next, add similar code to cart/index.js:
vuex-shopping-cart/src/shop/modules/product/index.js
... const cartModule = { country, mutations, actions, getters } export default cartModule; This exports the module equally cartModule.
Setting up the Store
With the state, mutations, actions, and getters all ready up, the last part of integrating Vuex into your awarding is creating the store. Here yous volition harness the Vuex modules to split your application store into ii manageable fragments.
To create your shop, open up the index.js file in your store folder:
- nano src/shop/index.js
Add the following highlighted lines:
vuex-shopping-cart/src/store/index.js
import { createStore } from 'vuex' import production from './modules/production' ; import cart from './modules/cart' ; export default createStore ( { modules : { product, cart } } ) Save the file, so exit the text editor.
You have now created the methods needed for state management and have created the store for your shopping cart. Next y'all volition create user interface (UI) components to swallow the data.
Pace 4 — Creating Interface Components
Now that you have the store for your shopping cart ready, you tin movement onto making the components for the user interface (UI). This volition include making some changes to the router and making forepart-end components for your navigation bar and listing and item views of your products and your cart.
First, yous will update your vue-router setup. Remember that when y'all used the Vue CLI tool to scaffold your application, you chose the router selection, which allowed Vue to automatically set up the router for y'all. Now you can re-configure the router to provide paths for Cart_List.vue and Product_List.vue, which are Vue components you will make later.
Open upward the router file with the following command:
- nano vuex-shopping-cart/src/router/index.js
Add together the following highlighted lines:
vuex-shopping-cart/src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router' import CartList from '../components/cart/Cart_List.vue' ; import ProductList from '../components/product/Product_List.vue' ; const routes = [ { path : '/inventory' , component : ProductList } , { path : '/cart' , component : CartList } , { path : '/' , redirect : '/inventory' } , ] const router = createRouter ( { history : createWebHashHistory ( ) , routes } ) export default router This creates the /inventory route for your products and the /cart route for the items in your cart. Information technology also redirects your root path / to the product view.
One time you take added this code, relieve and close the file.
Now you can set up your UI component directories. Run this command on your terminal to motility to the component's directory:
- cd src/components
Run this command to create three new sub-folders under the component's directory:
- mkdir cadre cart product
core will concord essential parts of your application, such as the navigation bar. cart and product volition hold the item and list views of the shopping cart and the total inventory.
Under the core directory, create the Navbar.vue file by running this control:
- touch core/Navbar.vue
Nether the cart directory, create the files Cart_List_Item.vue and Cart_List.vue:
- touch cart/Cart_List_Item.vue cart/Cart_List.vue
Finally, under the product directory, create these 2 files:
- touch production/Product_List_Item.vue product/Product_List.vue
At present that the file structure has been outlined, you tin can move on to creating the individual components of your front end-end app.
Navbar Component
In the navbar, the cart navigation link volition display the quantity of items in your cart. You will use the Vuex mapGetters helper method to directly map store getters with component computed properties, allowing your app to get this data from the store's getters to the Navbar component.
Open up the navbar file:
- nano cadre/Navbar.vue
Supervene upon the code with the following:
vuex-shopping-cart/src/components/core/Navbar.vue
<template> <nav class = "navbar" part= "navigation" aria-label= "primary navigation" > <div course = "navbar-brand" > <a part= "push button" class = "navbar-burger burger" aria-characterization= "menu" aria-expanded= "false" information-target= "navbarBasicExample" > <span aria-hidden= "truthful" > < /bridge> <bridge aria-subconscious= "true" > < /span> <span aria-hidden= "true" > < /bridge> < /a> < /div> <div id= "navbarBasicExample" class = "navbar-menu" > <div grade = "navbar-end" > <div course = "navbar-particular" > <div course = "buttons" > <router-link to= "/inventory" class = "button is-main" > <strong> Inventory< /strong> < /router-link> <router-link to= "/cart" form = "push is-warning" > <p> Total cart items: <span> { {cartQuantity} } < /span> < /p> < /router-link> < /div> < /div> < /div> < /div> < /nav> < /template> <script> import {mapGetters} from "vuex" export default { name : "Navbar" , computed : { ... mapGetters ( [ 'cartQuantity' ] ) } , created ( ) { this .$store. dispatch ( "getCartItems" ) ; } } < /script> As a Vue component, this file starts out with a template element, which holds the HTML for the component. This snippet includes multiple navbar classes that utilise pre-made styles from the Bulma CSS framework. For more information, cheque out the Bulma documentation.
This also uses the router-link elements to connect the app to your products and cart, and uses cartQuantity as a computed property to dynamically keep rail of the number of items in your cart.
The JavaScript is held in the script chemical element, which also handles land management and exports the component. The getCartItems action gets dispatched when the navbar component is created, updating the store land with all the cart items from the response data received from the server. Afterwards this, the store getters recompute their return values and the cartQuantity gets rendered in the template. Without dispatching the getCartItems action on the created life bike hook, the value of cartQuantity volition be 0 until the store land is modified.
Salve and close the file.
Product_List Component
This component is the parent to the Product_List_Item component. It will be responsible for passing downward the product items as props to the Product_List_Item (child) component.
First, open upwardly the file:
- nano product/Product_List.vue
Update Product_List.vue as follows:
vuex-shopping-cart/src/components/product/Product_List.vue
<template> <div class = "container is-fluid" > <div class = "tile is-antecedent" > <div class = "tile is-parent" five- for = "productItem in productItems" :key= "productItem.id" > <ProductListItem :productItem= "productItem" / > < /div> < /div> < /div> < /template> <script> import { mapGetters } from 'vuex' ; import Product_List_Item from './Product_List_Item' export default { name : "ProductList" , components : { ProductListItem :Product_List_Item } , computed : { ... mapGetters ( [ 'productItems' ] ) } , created ( ) { this .$store. dispatch ( 'getProductItems' ) ; } } ; < /script> Similar to the Navbar component logic discussed earlier, hither the Vuex mapGetters helper method directly maps store getters with component computed properties to get the productItems data from the store. The getProductItems activeness gets dispatched when the ProductList component is created, updating the store state with all the product items from the response data received from the server. After this, the store getters re-computes their return values and the productItems gets rendered in the template. Without dispatching the getProductItems action on the created life cycle hook, there volition be no product item displayed in the template until the store state is modified.
Product_List_Item Component
This component will be the direct kid component to the Product_List component. It will receive the productItem data as props from its parent and return them in the template.
Open up Product_List_Item.vue:
- nano product/Product_List_Item.vue
Then add the following code:
vuex-shopping-cart/src/components/product/Product_List_Item.vue
<template> <div form = "carte du jour" > <div course = "card-content" > <div class = "content" > <h4> { { productItem.title } } < /h4> <a form = "button is-rounded is-pulled-left" @click= "addCartItem(productItem)" > <stiff>Add to Cart< /potent> < /a> <br / > <p form = "mt-four" > { { productItem.description } } < /p> < /div> <div class = "media" > <div grade = "media-content" > <p course = "championship is-6" > { { productItem.owner } } < /p> <p form = "subtitle is-seven" > { { productItem.email } } < /p> < /div> <div grade = "media-right" > <a class = "button is-principal is-light" > <strong>$ { { productItem.toll } } < /strong> < /a> < /div> < /div> < /div> < /div> < /template> <script> import {mapActions} from 'vuex' export default { name : "ProductListItem" , props : [ "productItem" ] , methods : { ... mapActions ( [ "addCartItem" ] ) , } , } ; < /script> In addition to the mapGetters helper function used in the previous components, Vuex also provides you lot with the mapActions helper function to directly map the component method to the store'southward deportment. In this case, you use the mapAction helper function to map the component method to the addCartItem action in the shop. Now you tin can add items to the cart.
Save and shut the file.
Cart_List Component
This component is responsible for displaying all the production items added to the cart and also the removal of all the items from the cart.
To create this component, first open up the file:
- nano cart/Cart_List.vue
Next, update Cart_List.vue every bit follows:
vuex-shopping-cart/src/components/cart/Cart_List.vue
<template> <div id= "cart" > <div form = "cart--header has-text-centered" > <i form = "fa fa-2x fa-shopping-cart" > < /i> < /div> <p v- if = "!cartItems.length" form = "cart-empty-text has-text-centered" > Add some items to the cart! < /p> <ul> <li form = "cart-item" five- for = "cartItem in cartItems" :key= "cartItem.id" > <CartListItem :cartItem= "cartItem" / > < /li> <div form = "notification is-success" > <button course = "delete" > < /button> <p> Total Quantity: <span grade = "has-text-weight-bold" > { { cartQuantity } } < /span> < /p> < /div> <br> < /ul> <div class = "buttons" > <button :disabled= "!cartItems.length" class = "button is-info" > Checkout ( <bridge class = "has-text-weight-bold" >${ { cartTotal } } < /span> ) < /button> <button form = "button is-danger is-outlined" @click= "removeAllCartItems" > <span>Delete All items< /bridge> <span class = "icon is-small-scale" > <i class = "fas fa-times" > < /i> < /span> < /button> < /div> < /div> < /template> <script> import { mapGetters, mapActions } from "vuex" ; import CartListItem from "./Cart_List_Item" ; export default { proper name : "CartList" , components : { CartListItem } , computed : { ... mapGetters ( [ "cartItems" , "cartTotal" , "cartQuantity" ] ) , } , created ( ) { this .$store. acceleration ( "getCartItems" ) ; } , methods : { ... mapActions ( [ "removeAllCartItems" ] ) , } } ; < /script> This code uses a v-if argument in the template to conditionally render a message if the cart is empty. Otherwise, it iterates through the store of cart items and renders them to the folio. You lot as well loaded in the cartItems, cartTotal, and cartQuantity getters to compute the data properties, and brought in the removeAllCartItems action.
Save and close the file.
Cart_List_Item Component
This component is the directly child component of the Cart_List component. It receives the cartItem data every bit props from its parent and renders them in the template. It is also responsible for incrementing and decrementing the quantity of items in the cart.
Open up up the file:
- nano cart/Cart_List_Item.vue
Update Cart_List_Item.vue as follows:
vuex-shopping-cart/src/components/cart/Cart_List_Item.vue
<template> <div course = "box" > <div class = "cart-item__details" > <p grade = "is-inline" > { {cartItem.title} } < /p> <div> <span class = "cart-item--toll has-text-info has-text-weight-bold" > ${ {cartItem.price} } Ten { {cartItem.quantity} } < /span> <span> <i grade = "fa fa-arrow-circumvolve-up cart-item__modify" @click= "addCartItem(cartItem)" > < /i> <i form = "fa fa-arrow-circumvolve-down cart-item__modify" @click= "removeCartItem(cartItem)" > < /i> < /span> < /div> < /div> < /div> < /template> <script> import { mapActions } from 'vuex' ; consign default { name : 'CartListItem' , props : [ 'cartItem' ] , methods : { ... mapActions ( [ 'addCartItem' , 'removeCartItem' ] ) } } < /script> Here, you are using the mapAction helper office to map the component method to the addCartItem and removeCartItem actions in the shop.
Relieve and shut the file.
Lastly, y'all volition update the App.vue file to bring these components into your app. First, move dorsum to the root folder of your project:
- cd ../..
Now open the file:
- nano src/App.vue
Replace the contents with the following lawmaking:
vuex-shopping-cart/src/App.vue
<template> <div> <Navbar/ > <div class = "container mt-6" > <div form = "columns" > <div class = "column is-12 column--align-center" > <router-view> < /router-view> < /div> < /div> < /div> < /div> < /template> <script> import Navbar from './components/core/Navbar' export default { proper name : 'App' , components : { Navbar } } < /script> <fashion> html, body { height : 100 % ; background : #f2f6fa; } < /style> App.vue is the root of your application defined in Vue component file format. Once y'all have made the changes, relieve and close the file.
In this step, yous set up the frontend of your shopping cart app by creating components for the navigation bar, the product inventory, and the shopping cart. You lot also used the store actions and getters that you created in a previous step. Next, y'all will become your awarding up and running.
Step 5 — Running the Awarding
Now that your app is ready, y'all can outset the development server and try out the final product.
Run the post-obit control in the root of your front-end project:
- npm run serve
This volition outset a development server that allows you to view your app on http://localhost:8080. Also, brand sure that your backend is running in a split up terminal; yous tin do this by running the following command in your cart-backend projection:
- node server
Once your backend and your frontend are running, navigate to http://localhost:8080 in your browser. You will notice your functioning shopping cart awarding:
Conclusion
In this tutorial, you built an online shopping cart app using Vue.js and Vuex for data management. These techniques can exist reused to course the basis of an eastward-commerce shopping awarding. If you would like to learn more about Vue.js, check out our Vue.js topic page.
lombardotheirried.blogspot.com
Source: https://www.digitalocean.com/community/tutorials/how-to-build-a-shopping-cart-with-vue-3-and-vuex
0 Response to "The Following Baby Can Not Buy, Please "Buy Now" Under the Order, or Re-add the Shopping Cart Order"
Post a Comment