e-commerce cart example using Vue.js

I’d like to show how to basically setup basic a cart frontend with Vue (2.x), REST API (Axios in example) written in ES6. I use single file components, which you also should for performance reasons. I will also use BEM for class naming, but will not include any css, which is redundant.

We will start from the smallest component (single product), then we will create a list container for products and necessary services. In the end will create and connect services to vue components.

Examples are also much simplified. I based on one of my projects in creativestyle and removed all code not necessary for this example.

Single product component

Each e-commerce cart contains similar products list view. This is how my syntax looks:

In <template></template> I paste my HTML markup. Properties with a colon, like :src take object from props, data or computed bound to this, which is the component scope.

In <script></script> i just paste my component data. Later in parent component or bundle, I will import it and add to vue instance via Vue.component('basket-product', basketProductObj); . name is important for debugging process, props are attributes which can be injected into component from parent. In this example I made stateless component without any logic. I will add it later (or not :>).

Now we can render list of our products:

Here you can see:

  • <slot> element, which prints static content from outside of the component. In this case server prints CMS message or any other content if there are no products.
  • <loader> is another Vue component, which is just overlay with a spinner, to show some action when products are refreshing. It’s syntax doesn’t matter.
  • <basket-product> where we print our previously created component in v-for loop. We pass product-data inside, so our data.products must be array of objects, containing properties for basket-product component.
  • data.loaded will be our flag which we will turn true when products will be during loading and false if loaded.

So far we have no data connected, so it will print an empty cart slot.

Connecting data via event-bus (publish/subscribe pattern)

Vue instance is shipping with event bus. The simplest implementation looks like this:

We use this event bus for all global and multi-component communication. If used properly, it reduces component coupling and creates simple communication process. eventBus contains $on, $emit and $once methods. For example:

eventBus.$on( 'componentFoo:action', (data) => callback(data) ); — will subscribe on an action (where component:action is only my convention for namespace) and trigger some callback with data it will ship.

eventBus.$emit( 'componentFoo:action', {foo: 'bar'} ); will publish same event as above, passing some payload inside (so callback will get it). Always pass objects, even if it has only one property at the beginning! It will be mess later if you need to change it

eventBus.$once is the same as $on, but will be trigger once and will unsubscribe handler.

Creating service to fetch cart data

Example of basket service component:

I explained in snippet’s code it’s behavior. Basically we fetch data from server via AJAX every time basket:changed is called.

Now we should connect the data with products-list. We will add few lines more.

So we:

  • Import eventBus.
  • Publish information that basket is loaded by vue ( created() lifecycle hook). No basket service will do first load of products
  • Listen to basket:loading and show loader
  • Listen to basket:updated and pass products to our template. Also we hide loader.

Most of work is done.

Now you have possibility to emit basket:changed wherever you like, to trigger whole system to update. You can also subscribe for it minicart and other connected components.

Deleting product example

We need to update our markup of basket-product. To last line within <template> tag, we gonna add simple <span>X</span>. To connect some event for it, we will use Vue click event (Vue wraps native event with it’s own and also provide custom events). It will (without css class, you can handle on your own) look like:

<span @click="_removeClickHandler">X</span>

In our <script> we need to add the handler:

methods: {
_removeClickHandler() {
  // We will do some DELETE ajax here

We can assume, that API needs itemId provided into DELETE body, so we will pass one from the props. We can create and import some delete-product service here and call api directly from product component. But we will move this logic to products list (and can be even higher), to keep product component simple. Here is the product component with added custom event:

What we did here is just notifying parent component that some action happen in component. We can use a lot nice stuff with this approach, for example callback input on validation error etc.

Now just add event callback in products-list . This should be our final code:

As you can see we added a callback to custom basket-product event via @remove="callback" . Cool. We got only product service to create (the one we just imported).

Nothing new here, we create just another call to server and when it deletes product in backend, we emit basket:changed event. It will trigger new basket download in a basket service and update our list with new products.

You can go deeper with optimization and get (if possible) new basket from delete (or add, or edit or any other) action’s response, but I can’t handle everything here ;)

In the same way you can add editing product’s properties, adding to wishlist and any other actions. It’s very scalable.

Of course this solution’s performance can be much improved, to prevent a lot of API calls, but this is big topic.

The reason for writing this article was showing the use of event bus, custom events and some service-api-component communication in some real life example. I hope it’s clear and I didn’t make some mistakes during simplifying actual code.

Let me know what you think.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.