Building an Online Store with Vue CLI — Part 2

Introducing basic Vue and Vuex principles

Nathan Magyar
9 min readJan 10, 2019

Updated 6.25.20

Part 1 | Part 2:

In the previous post, I walked through the steps of setting up a new project with Vue CLI. This time, we’ll focus on displaying products on the homepage.

A brief explanation of “state”

Store.js is where we will keep all the data we want to display in our application. By default, Vue CLI creates a new Vuex store that consists of three parts: state, mutations, and actions.

The “state” object, inside our “store,” is where all of our application-level data will reside. Commonly referred to as the “single source of truth” for our app, it serves as one large tree that logically organizes all the information we might need to access from other places in our app; things like user account information, product inventory, items that are in the user’s cart, etc.

To change the contents of the “state” object, such as when a user adds an item to her cart, we use the other two parts of the “store”: actions and mutations . Actions and mutations both categories of functions; actions trigger or “commit” mutations, and mutations trigger a change in the state object. So action --> mutation --> updated state.

If actions and mutations are so similar, why do we have to use them both?

I’m glad you asked 😉. Well for one thing, it’s a bit of a “you can’t have one without the other” situation. The only way to update state is to commit a mutation, and the only way to do that is with an action. Having actions and mutations separate also makes the code more reusable. Mutations only have to care about affecting the state in a certain way, while actions can focus on taking care of the business logic (deciding which mutation to commit). This way, the same mutation can be used by multiple actions, and one action can make lots of different changes to the state at once by committing several mutations all together.

How it all works

To quickly recap, when a user performs an action on our page (or, in the “View” stage below), they trigger or “dispatch” an action, which commits a mutation and updates the state, which in turns gets passed back into the View in the form of a refreshed store state.

Vuex state management illustration from https://vuex.vuejs.org/

Working with Single File Components

In addition to understanding state management, it’s also important to know the basic structure of single file components in Vue. Any file ending in .vue has three parts: template, script, and style.

  • The template contains the component’s html, which must be wrapped in one singular outermost element, immediately within the <template> tags. There cannot be more than one child at this level. Inside of that, though, go nuts!
<template
<div>
<h1>Add as many child elements as you waaaaant!</h1>
<p>BAM</p>
<p>BAM</p>
<p>BAM</p>
</div>
</template>
  • The script section is for JavaScript, which defines any component-specific data and behavior.
  • The style section is where you write CSS, which can be globally or locally scoped, depending on if you include the “scoped” attribute on the style tag or not. By adding the “scoped” attribute, any styles you write will only apply to that component. Excluding this attribute will let you draw on one component’s styles in another.

At minimum, a component needs a template section. The other two are optional.

Ok, enough background info. Let’s get started!

Displaying our inventory

Step 1: Populate our store state with dummy products

For us, our state object will have two keys, “cart” and “products.” The “cart” key points to an empty array for now, since a user won’t have any items in her cart when the app first loads. The “products” key contains an array of all the product objects in our inventory. As you see below, each product object contains various data points, such as the product name, a unique id, price, etc. Store.js should look as follows:

Step 2: Gather and save images

You might’ve noticed that each of our product objects contains an “image” key that points to an array of image files. To get these images, visit this Flickr album or feel free to find your own (I borrowed mine from Banana Republic. Thanks BR 🍌). Either way, download the images you want to use and make sure the names match what’s written in the images arrays (“53362–1.jpg”, “53362–2.jpg”, “53362–3.jpg”, etc).

To save the images in the correct place within the app, open “src,” and then inside “assets” create an “img” folder. Then, create one more folder named “products.” There is where you should save the images:

online-store/src/assets/img/products/53362-1.jpg

The reason we’re storing and referring to the images in this way is that we don’t know how many images a product might have, so it’s easiest to just keep them all in the same folder and store the file names with each product object.

Update: It appears Flickr doesn’t retain the original file names, so you have to spend a minute or so renaming them to match what’s written in the images array of each product item in store.js. Sorry! Please also feel free to adjust the number of image paths in the store to fit however many images you want to use.

Step 3: Display store state on the homepage

Open Home.vue and delete most of its contents until it looks like this:

Your browser should now appear as so:

Empty page after deleting most of the contents of Home.vue

After declaring the “name” of the component, add a new object for computed properties with a key of “computed.” Inside the object, add a function (aka computed property) called “products”, which will return the array of all the products in our inventory. Then, use Vue’s template syntax to display the array of products in our template (line 3).

Your output might look slightly different, as these objects don’t have the “images” array includes.

Now let’s break this down.

What is a computed property?

Computed properties allow us to handle complex Javascript logic in a more elegant, semantic, readable way. Instead of using the above approach, imagine we did this:

It still works, but it’s not as clear what information the template is displaying. Plus, imagine if we needed to perform some conditional logic before displaying a final piece of data, like only showing featured products or products below a certain price. Doing all that in the template is not only hard to read, but also not reusable. What if we wanted to display the same information multiple times in the same component? 😵 We’d be left with code that would be just as hard to maintain as it would be to read. Thus, computed properties.

What are those curly braces for?

That is Vue’s template syntax at work. By surrounding a string of text with double curly braces, you’re telling Vue that the contents aren’t just any old string. Instead, the contents refer to a piece of Javascript which is either evaluated right there inline or written in the adjacent <script> section, in our case: the products computed property.

An example of an inline expression would be:

<div>
{{ 1 + 1 }}
</div>

The above would produce the string “2” on the screen. Read more about template syntax here.

How can you just put a function inside an object with no key?

It’s actually just a shorthand syntax that’s available with ES6. An alternative, and equally valid but longer, way to write the same thing above is:

Directly above, we’re using an anonymous function as a value that belongs to the “product” key, all of which can be shortened to the other example. Your choice!

Step 4: Improve the template

Now let’s get the products to properly display on the page. To do so, we will use a “directive” called “v-for” to loop through the items in our products computed property array. Inside the loop for each item, we’ll access the product’s name, image and price. The code and output look like this:

{{ products }} computed property items displayed with HTML.

Breaking it down again:

  • The contents of the v-for directive on line 6 determine what we’re looping through. The syntax of “product in products” can be read as “for every product in the array of products we have…do something.” We want to create an <li> for each product, so that’s why we place the v-for on that element. Products is the computed property that returns the array over which we’re looping, and product is an “alias” or a temporary, arbitrary name for the item we’re on in each loop (we’ve could’ve called product anything, but the word I chose makes the most sense to me). Also of critical importance is the “key” attribute on line 6, which is specific to v-for. The “key,” some sort of unique identifier (for us, the product id), helps Vue keep track of which item in the array it’s rendering/changing, so things can run as efficiently as possible. Instead of rerendering the whole page if a particular item changes, Vue will just update that one specific part (identified by the “key”).

Note: in order to insert a piece of data as the value for an html attribute, the attribute itself must have a colon before it (“:key=product.id”, short for “v-bind:key”). “key=product.id” will just evaluate to the string “product.id” whereas including the colon evaluates to the product’s id, “533642” for example.

  • Lines 8 and 9 use the same template syntax we’ve seen previously to extract specific information about a product. Note that we can use dot notation because each product is an object with those properties.
  • Line 5 is the most unusual part of putting this together. For reasons beyond the scope of this tutorial, we can’t just insert a simple image path to the “src” attribute on the image. It has to be wrapped in a “require()” method (line 26), which is something outside of the standard Javascript API. For better readability, I created a method on lines 25–27 to create the necessary path to the image, which ultimately inserts the first image file for each product in the “images” array. The reason this has to be a method and not a computed property is that we’re passing the function an argument, the product. Since computed properties don’t accept arguments and methods do, we have to use the latter.
  • Line 26 also employs another nifty ES6 trick. By using backticks (located above the tab key on your keyboard) instead of double or single quotes, you can add interpolated values to the string with this syntax ${ someVariable }. Way easier to type than concatenating strings!

Step 5: Add a bit of style

At the bottom of the Home.vue file, add a <style> section. Specify the CSS preproccessing language to be “SCSS” with the attribute lang=”scss”. Add styles that:

  • create a narrower, centered page width for the overall content (“wrapper” class)
  • a “featured-items” flex-parent class that strips the <ul> of any default styles
  • a “featured-items__item” class with a width property and centered text
  • a “product-image” class with a fixed height
  • a “product-title” class that changes the font-weight to bold
  • optionally, add media queries for better responsive behavior

In App.vue, add the “wrapper” class to the “nav” div so it follows the same page margins, and remove the text-align property on the #app styles (line 16 previously).

The output should look something like:

{{ products }} computed property with SCSS added.

Recap

Woohoo! We learned a lot in this post:

  • State management terms and data flow
  • Single File Component structure
  • Template Syntax
  • The v-for directive
  • Computed Properties
  • Methods

Next up, we’ll add product detail pages to our app and connect them with routes. See you then!

--

--

Nathan Magyar

User Experience Designer and Front End Developer, University of Michigan Office of Academic Innovation