Build a shopping cart with Vue 2 and Vuex

Build a shopping cart with Vue 2 and Vuex

<strong>This tutorial goes through building out a shopping cart application with Vue.js and state management using Vuex. Users can browse products, add products to cart and view the total cost of their order. We set up routing using vue-router</strong>

This tutorial goes through building out a shopping cart application with Vue.js and state management using Vuex. Users can browse products, add products to cart and view the total cost of their order. We set up routing using vue-router

**tl;dr **— 🐙 source code, 🛒 demo

In a previous tutorial we went through the basics of setting up a todo application with Vue.js. In this tutorial we will build out a shopping cart application with Vue.js and Vuex. Vuex is a state management library for Vue.js. This means that it helps us keep application state in sync across multiple components and routes through a single source of truth, called a store. In our shopping cart application users will be able to view products, add them to their cart and view the total cost of their order. We will handle routing on the frontend using the official vue-router package.

📙 Why Vuex and what the heck is state management?

Vuex is maintained by the Vue.js core team and brands itself as centralized state management for Vue.js. It is similar to Redux or Flux, application development techniques pioneered by Facebook and the React.js community. If you are unfamiliar with these libraries, not to worry! The Vuex library is for maintaining a global Store that knows about all of the state within our application. In the long run this can make development easier because state is modified and collected from one place instead of many. These changes are then propogated down to the individual components primarily through computed properties.

The source code for the Vuex shopping cart application is available 🐙 here (if you dig the repo give it a ⭐)

Vuex adds some additional complexity upfront but in the long term can make our application development easier, cleaner and faster. If you write client side tests for your application having a single state tree through Vuex can also make the application simpler to reason about.

🔀 Alternative approaches

Without Vuex, you could communicate state across components using events and passing properties as in the diagram below. This works well for simple parent to child communication and chances are you already use it in your Vue.js applications!

A more comprehensive approach to state management from the ground up would be to create a store object, attach it to the global namespace and read/write from that.

state management alternate approach:

window.store = { tasks: [{ id: 1, title: 'Learn Vuex' }]};
const vmA = new Vue({
  computed: {
    todos(){
      return store.tasks
    }
  },
  methods: {
    addTodo(newTodo){
      store.tasks.push(newTodo)
    }
  }
})
const vmB = new Vue({
  computed: {
    todos(){
      return store.tasks
    }
  },
})

Vuex builds on top of this starting point and adds new concepts like getters, mutations and actions. Let’s take a look at how it works by building our shopping cart application.

🌲 The Vuex Part: Create a new Vue app

The first step is to use the Vue CLI that is maintained by the core team. We’re going to use the webpack-simple scaffold.

$ vue init webpack-simple vuex-shopping-cart 

Once you have generated a new application, modify the .babelrc file like so:

“presets”: [
 [“env”, { “modules”: false }],
 “stage-3”
]

This gives us the ability to use the ES6 spread operator in our code. You can read more about the spec here.

Install some packages that we will need for the build and the application.

$ npm i vuex vue-router — save
$ npm i babel-preset-stage-3 style-loader babel-plugin-transform-object-rest-spread — save-dev

The babel stuff isn’t strictly required but can make our lives easier down the line. I generally try not to concern myself with webpack and build stuff but in this case it helps.

We’re going to use Bulma for styling so add that with font awesome to index.html.

<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel=’stylesheet’ href=’https://cdnjs.cloudflare.com/ajax/libs/bulma/0.5.2/css/bulma.min.css'>

🏗️ Build the Vuex application

Now that we have the application generated let’s build out the rest of the shopping cart.

The starting point for our application is in src/main.js.

import Vue from 'vue'
import VueRouter from 'vue-router'
import store from './store/index.js'
import App from './App.vue'
import Products from './components/Products.vue'
import Cart from './components/Cart.vue'
Vue.use(VueRouter)
// Define routes
const routes = [
  { path: '/', component: Products },
  { path: '/cart', component: Cart }
]
// Register routes
const router = new VueRouter({
  routes
})
new Vue({
  el: '#app',
  render: h => h(App),
  router,
  store
})

In this file we add code to register our routes and components. We also attach the store to the global Vue app. If you are unfamiliar with ES6 syntax, store in an object is the same as store: store

In our main App.vue component we will register the base template for our Vue application complete with the NavBar.

src/App.vue

<template>
  <div id="app" class='container'>
    <nav-bar></nav-bar>
    <router-view></router-view>
  </div>
</template>
<script>
import NavBar from './components/NavBar.vue'
export default {
  name: 'app',
  components: {
    NavBar
  },
  data () {
    return {}
  }
}
</script>

The product and cart pages will render in the <router-view> element. We can come back to the NavBar a little later, but the full source code for it is available here.

🍄 Mutations

Now that we have our application scaffold set up, create a new folder called src/store that will have an index.js and a mutation-types.jsfile. The mutation types file defines what mutations can occur within our application:

export const ADD_TO_CART = 'ADD_TO_CART'

What is a mutation you ask? Mutations change the state of our application. In this case the one thing our application does is modifies the cart state to feature new products as customers add them. Mutations must be synchronous, so no ajax calls belong in mutations.

We have the mutation-types.js file so that our mutations are defined in one place and we can see at a glance all the mutations to the state our app can possibly make.

In the src/store/index.js define our mutations and register our store:

import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation-types'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
// mutations
const mutations = {
[types.ADD_TO_CART] (state, { id }) {
      const record = state.added.find(p => p.id === id)
if (!record) {
        state.added.push({
          id,
          quantity: 1
        })
      } else {
        record.quantity++
      }
    }
}
// one store for entire application
export default new Vuex.Store({
  state,
  strict: debug,
  getters,
  actions,
  mutations
})

🏋️ State, Getters and Actions

In the above code we are missing values for state, getters and actions. Those are defined below:

// initial state
const state = {
  added: [],
  all: [
    {
      id: 'cc919e21-ae5b-5e1f-d023-c40ee669520c',
      name: 'COBOL 101 vintage',
      description: 'Learn COBOL with this vintage programming book',
      price: 399
    },
    {
      id: 'bcd755a6-9a19-94e1-0a5d-426c0303454f',
      name: 'Sharp C2719 curved TV',
      description: 'Watch TV like never before with the brand new curved screen technology',
      price: 1995
    },
    {
      id: '727026b7-7f2f-c5a0-ace9-cc227e686b8e',
      name: 'Remmington X mechanical keyboard',
      description: 'Excellent for gaming and typing, this Remmington X keyboard ' +
        'features tactile, clicky switches for speed and accuracy',
      price: 595
    }
  ]
}
// getters
const getters = {
  allProducts: state => state.all, // would need action/mutation if data fetched async
  getNumberOfProducts: state => (state.all) ? state.all.length : 0,
  cartProducts: state => {
    return state.added.map(({ id, quantity }) => {
      const product = state.all.find(p => p.id === id)
return {
        name: product.name,
        price: product.price,
        quantity
      }
    })
  }
}
// actions
const actions = {
  addToCart({ commit }, product){
    commit(types.ADD_TO_CART, {
      id: product.id
    })
  }
}

To start with are instantiating a global state with three products and setting our cart (added) to an empty array. If you wanted to load these products in via an ajax call we would require additional mutations to add in products to the state. For the sake of simplicity we are setting up the state with our products statically defined.

Getters are for accessing state values. The official docs define getters as similar to computed properties for stores. In the above code we have a getter to access the products, the number of products and the products within our cart. The map function creates an array specifically for the added key that retains information about the quantity of a particular product the customer would like to order.

🏢 Vue.js Components

Now that we have defined our routes and store it’s time to build out the components for our application. In the src/components folder add Cart.vue, Products.vue and NavBar.vue files.

The Products component will render a list of the products for sale and feature “Add to Cart” buttons for customers to buy the products.

src/components/Product.vue

<template>
  <div>
    <h1 class="title">All Products</h1>
      <p>{{length}} products</p>
      <table class="table is-striped">
        <thead>
          <tr>
            <th>Name</th>
            <th>Description</th>
            <th>Price</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="product in products" track-by="id">
            <td>{{product.name}}</td>
            <td>{{product.description}}</td>
            <td>${{product.price}}</td>
            <td><button @click='addToCart(product)' class='button is-info'>Add to cart</button></td>
          </tr>
        </tbody>
    </table>
  </div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
  name: 'app',
  computed: mapGetters({
    products: 'allProducts',
    length: 'getNumberOfProducts'
  }),
  methods: mapActions([
    'addToCart'
  ])
}
</script>

The mapGetters function pairs keys to the results of the getter functions. This means that in our local component state their are products and length values that map to our global store. These values are reactive so that if our state updates within another component these values will also be updates. The mapActions helper maps local methods to the actions we defined within our store.

We can see the value of this when we define our NavBar. In the NavBar we want to show the number of products the user currently has in their cart. To do this we will read from the same state as the products component. Instead of communicating state and forth between siblings and children they read from a single source of truth, the store.

The Javascript for the NavBar looks like this:

src/components/NavBar.vue

import { mapGetters } from 'vuex'
export default {
  computed: {
    itemsInCart(){
      let cart = this.$store.getters.cartProducts;
      return cart.reduce((accum, item) => accum + item.quantity, 0)
    }
  }
}

And the relevant HTML within the Vue template:

<div class="nav-item is-tab" :class="{ 'active-bottom-border': $route.path === '/cart' }">
  <div class="field is-grouped">
    <p class="control">
      <router-link to='/cart' class="button is-info">
        <span class="icon">
          <i class="fa fa-shopping-cart"></i>
        </span>
        <span>Checkout ({{itemsInCart}})</span>
      </router-link>
    </p>
  </div>
</div>

The itemsInCart value draws its data from the store and the get cartProducts call. We make modifications to the data in our computed property so that we display the current number of items the customer would like to purchase. The classes here add in styling from the Bulma CSS library and the <router-link> components render anchor tags for navigation.

Finally, we have the Cart component for rendering the items the user would like to buy and the total cost for their order.

src/components/Cart.vue

<template>
  <div class="cart">
    <h1 class="title">Your Cart</h1>
    <p v-show="!products.length">
      <i>Your cart is empty!</i>
      <router-link to="/">Go shopping</router-link>
    </p>
    <table class="table is-striped" v-show="products.length">
      <thead>
        <tr>
          <td>Name</td>
          <td>Price</td>
          <td>Quantity</td>
        </tr>
      </thead>
      <tbody>
        <tr v-for="p in products">
            <td>{{ p.name }}</td>
            <td>${{ p.price }}</td>
            <td>{{ p.quantity }}</td>
          </tr>
          <tr>
            <td><b>Total:</b></td>
            <td></td>
            <td><b>${{ total }}</b></td>
          </tr>
      </tbody>
</table>
    <p><button v-show="products.length" class='button is-primary' @click='checkout'>Checkout</button></p>
  </div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters({
      products: 'cartProducts'
    }),
    total () {
      return this.products.reduce((total, p) => {
        return total + p.price * p.quantity
      }, 0)
    }
  },
  methods: {
    checkout(){
      alert('Pay us $' + this.total)
    }
  }
}
</script>

Here we use computed properties with the mapGetters helper function to bind the added value of the store to products in the local component scope. We read this one to our calculate our total in a value local to this component but that derives its information from the global store. Lastly, we add a method for checkout that shows an alert for the total amount of money the user owes. If you are interested in building out real life payment processing you can view this tutorial.

💥 Conclusion

Vuex is a state management package for Vue.js that handles modifications to the state tree within a single source of truth called aptly the State. The State can only be modified with Mutators which must be syncronous functions. To run asynchronous functions or perform other tasks we can define Actions which can be called from components and ultimately call Mutators. We can access the State values within components through Getter functions. The mapGetters and mapActions can simplify our component definitions.

Though Vuex brings in many new concepts to Vue.js application development it can be very helpful in managing complex application state. It is not necessary or required for many Vue.js applications but learning knowing about it can make you a better, more productive developer.

Recommended Courses:

Vue.js 2 Essentials: Build Your First Vue App

Getting started with Vuejs for development

Horizontal Feed Component with Sass and VueJs 2

Learn Vue 1 JS introduction to simple reactive JavaScript

Real Time Chat With Laravel Broadcast, Pusher and Vuejs

What are the differences between the various JavaScript frameworks? E.g. Vue.js, Angular.js, React.js

What are the differences? Do they each have specific use contexts?

What are the differences? Do they each have specific use contexts?

Ember.js vs Vue.js - Which is JavaScript Framework Works Better for You

Ember.js vs Vue.js - Which is JavaScript Framework Works Better for You

In this article we will discuss full details and comparison of both Ember.js and Vue.js

JavaScript was initially created to work for web applications. But today they have become the favorite of mobile app developers. Most of the developers prefer to work with frameworks based on JavaScript. It simplifies coding. You can use JavaScript with almost any framework.

The use of a particular framework will decide how easy and fast it is to create the app. So, you must choose the best one suited for the app that you are planning to build. You must make a wise choice so that you benefit in the end. Among the crowded market, two of the frameworks stand out. We will make a comparison between Ember.js and Vue.js.

Why Do You Select A Particular Framework?

Before we start comparing the two frameworks, we should understand the factors that lead to the choice of a framework. Each developer chooses a framework before he or she goes to work on an app. Let us see the reasons for the selection.

● The codes must be easy to understand and transparent.

● The framework should give the maximum power with the least amount of coding.

● The framework should provide a well laid out structure to work on.

● Does the framework support an in-built router or an external plug-in router?

● The framework should be able to transfer more data on a full page-load so that it becomes a single-page app. A single-page app is more beneficial for the application.

● In single page architectures if there is a need for users to share links to sub-screens within the interface, then the framework should have the capacity to route based on the URL.

● A tighter template option can help in enabling two-way binding.

● The framework should not conflict any third-party library.

● Testing the codes inside the framework should be easy.

● The framework should provide the HTTP client service for AJAX calls

● The documentation is essential. It should be complete and up-to-date.

● The framework should be compatible with the latest version of the browser.

● The framework has to fulfill the above conditions for easy construction of the app. You must ensure that the framework you choose meets the conditions.

Vue.js Explained

Developers are always looking at new frameworks to build their apps. The main requirements are speed and low cost. The framework should be easy to use by even new developers. You should be able to use it at low cost. Other considerations are about simple coding, proper documentation, etc.

Vue.js combines a lot of good when it comes to software language for web app development. The architecture of Vue.js is easy to put in use. The apps developed using Vue.js are easy to integrate with new apps.

Vue.js is a very lightweight framework. It makes it fast to download. It is also much faster than other frameworks. The single-file component nature of the framework is also beneficial. The size has made it very popular.

You can further decrease weight. With Vue.js you can separate the template-to-virtual DOM and compiler. You can only deploy the minified and zipped interpreter which is only 12 KB. You can compile the templates in your machine.

Another significant advantage of Vue.js is that it can integrate easily with existing applications created with JavaScript. It will make it easy for using this framework to make changes to applications already present.

Vue.js also integrates easily with other front-end libraries. You can plug in another library and make up for any deficiency in this framework. This feature makes this tool a versatile one.

Vue.js uses the method of rendering on the streaming-side server. You can render your component and get a readable stream. You can then send this to the HTTP server. It makes the server highly responsive. Your users will get the rendered content very quickly.

Vue.js is very SEO friendly. As the framework supports server-side rendering, the views are rendered directly on the server. The search engines list these.

But the most important thing for you is the ease with which you can learn Vue.js. The structure is elementary. Even new developers will find it easy to use it to build their apps. This framework helps in developing both small and large templates. It helps to save a lot of time.

You can go back and check your errors very easily. You can travel back and inspect all the states apart from testing your components. It is another important feature as far as any developer is concerned.

Vue.js also has very detailed documentation. It helps in writing your applications very quickly. You can build a web page or app with the basic knowledge of HTML or JavaScript.

● Vue.js has pure architecture. It helps in integration with other apps

● Vue.js is lightweight and fast. It can be made lighter by deploying only the interpreter

● You can separate the compiler and the template-to-virtual DOM.

● Due to smooth integration, you can use this to make changes to existing apps

● To make up for any shortfall, you can plug-in any library and makeup.

● As Vue.js uses streaming-side server rendering, your users can get quick responses.

● The server-side rendering also helps in being ranked higher by search engines.

● It has a simple structure. Easy to use for any new developer

● You can go back and check and correct your errors.

● You can check all the existing states.

● Detail documentation also helps build the web page or application very quickly.

Ember.js Decoded

Ember.js is an MVVM model framework. It is open-source software. This platform is mostly used for creating complex multi-page applications. It maintains up-to-date features without discarding any of the old features.

With this framework, you have to follow the architecture of the framework strictly. The JS framework is very tightly organized. It reduces the flexibility that other frameworks might offer.

There is a very refined and developed control system for its platforms and tools. You can integrate it with the new version with the tools provided. There is strict guidance about avoiding outdated APIs.

You can understand Ember’s APIs easily. They are also easy to work. You can make use of highly complex functionalities simply and straightforwardly.

The performance is better as similar jobs are processed together. It creates batches of similar bindings and DOM updates to improve the performance. It means that the browser needs to process them in one go. It will avoid recomputing for each task, wasting a lot of time.

You can write the codes in a simple manner and modules. You can use any of Ember’s APIs. It is possible due to the presence of Promises everywhere.

Ember comes with a well-written guide. The API is recorded in a useful manner. It is a front-end framework that is loaded. Ember has a router, pipeline, services, etc. of its own.

The basis for views, controllers, models, and framework is the Ember Object Model. All components come from the same objects. The framework is firm and steady. The reason is that all elements have similar jobs and characteristics.

Ember has made the general application, organization, and structure clear so that you don’t make any mistakes. You will have no chance to complicate the application unnecessarily. If you have to go out of the defined limits, you will have to force your way out.

The language used for templating in Embers is Handlebars. This language helps Embers to keep its logic out of view. The clean syntax of Handlebars makes it easy for you to read and understand the templates. Handlebar templates are faster to load.

Another advantage you gain from Handlebar is that you don’t have to update your template every time you add or remove data from the page. It will be done automatically by the language itself.

A community that is continually improving the framework supports Ember. They are updating the framework with the latest technology. They also make sure that backward compatibility is possible.

● Ember.js is an open-source MVVM model framework suitable for complex multiple-page applications.

● It offers both the latest and old features.

● It has a very tightly structured framework which doesn’t offer much flexibility

● A very refined control system helps you to integrate with new versions without any problem.

● There is strict guidance about avoiding outdated API versions.

● Ember’s APIs help you to use complex functionalities in a simple manner

● There is no recomputing for each task as the framework allows the browser to do similar functions together.

● Promises allow you to write modular and straightforward code using any API of Ember.js.

● Ember.js is a fully loaded, front-end framework.

● The framework is stable because all components have the same functionalities and properties.

● It has well-defined limitations which will prevent your complicating your application

● Handlebars, the language used by Ember.js allows you to read and understand templates easily. It also helps to load the templates faster.

● Handlebars will ensure to update the template every time you add or remove data.

● Ember.js has an active community that updates the framework regularly and facilitates backward compatibility.

A Comparison Between Ember.js And Vue.js

This article intends to compare the features of both frameworks. Let us see how the characteristics of these frameworks compare. It will help you to make use of the right framework for your web application.

When you need a modern engine for an old application, it is Vue.js which will help you. It combines the best properties of other frameworks. Vue.js is a developing framework. A ready-to-use library of interface elements does not exist. However, many third-party libraries can help you.

Ember.js offers you a well-organized and trustworthy framework. When the development team is big, this is the framework that suits best. It allows everyone to understand the written code and contribute to a common project. The technology will be up-to-date, and the platform will be stable.

Vue.js can help you use the syntax of different kinds. It helps in writing the codes with ease. It is also an SEO friendly framework. Ember is a fully loaded front-end framework and can help you develop the applications very fast. But it is not suitable for developing small projects.

It is not easy to say this is better than that. It will depend on what kind of project you have undertaken. Both have their pluses and minuses. The below table will help in a better comparison.

Final Thoughts

It is not easy to conclude as to which is better. It all depends on the application that you want to develop. Both frameworks are developing. Both are getting updates. Both the communities are working on the frameworks.

While Vue.js is more comfortable for writing codes, Ember is a full-stack framework allowing the development of apps very fast. It is suitable for big projects. It is too complicated to be used for smaller projects.

We hope you had a great time reading this article. If you’ve any questions or suggestions related to this blog, then feel free to ask them in the comment section. Thank You.!