Building an Online Store with Vue CLI — Part 6

Practicing Getters, Computed Properties, and Abstract Page Components

Nathan Magyar
6 min readFeb 12, 2019

Part 1 | Part 2 | Part 3 | Part 4 | Part 5 | Part 6:

In the previous article we used computed properties to mimic the checkout process for our online store. This post will give you more practice with computed properties, getters, and the process of creating a generic page component that displays products for a specific gender.

Step 1: Add more dummy data

Go ahead and copy/paste the following store.js file into your project. It includes 6 new products, and adds a new featured property to every object in the products array. The first four have their featured property marked as true (meaning we want to feature them) and the others have a value of false.

Test Your Knowledge: Quiz Question 1

You can find the updated products directory of images here. Make sure to place them in the same place: online-store/src/assets/img/products.

Cool! New products! Except when you view the homepage of your project…eek! Too many featured items:

Step 2: Create a featuredProducts getter

At the bottom of store.js inside your getters object, create a new getter to retrieve only store products that have featured marked as true.

Then, in Home.vue, replace the products computed property with one named featuredProducts. Its return value should be the featuredProducts getter you just created (aka an array of products whose featured property is true).

That’s looking better, but I preferred when we were only showing three products in this section. And…what if our store one day has a much higher number of products with featured marked as true? We’d wind up with the same scenario we just had after adding the dummy data.

Let’s fix this by limiting the number of number of featured products to three:

featuredProducts: function() {
return this.$store.getters.featuredProducts.slice(0,3)
}

On the end of the return statement add the slice method, which returns a new array of items based on a starting index and an ending index for the original array. Note: javascript will not include the item found at the ending index. It stops at the one just before it.

Test Your Knowledge: Quiz Question 2

Step 3: Create a GenderOverview page component

Next we will be making one page that is capable of displaying either women’s and men’s items only, depending on the URL parameters (aka if ‘men’ is in the URL or ‘women’ is in the URL). The reason we’re creating one component for both of these pages is because although they will have different products, their structure and behavior will be the same. This keeps our code DRY (don’t repeat yourself).

Inside the views directory create a component called GenderOverview.vue. For now you can give it a hardcoded title in the template:

In router.js, we’ll import the GenderOverview component and create a new route object that is similar to the one used for theproducts page. This time, instead of passing in a productId, we’ll pass in a string that specifies the gender of products to display for the page, hence the path of '/:gender/'.

Now let’s add a new router-link element to our navigation in App.vue. Remember that since it’s a dynamic route we have to use more complex syntax for router-link's to attribute. The added router-link below will take users to a page for women’s products:

Test Your Knowledge: Quiz Question 3

Step 4: Add gender-related computed properties

Since this component is designed to display products for men or women, it needs a way to find out which gender of products the user wants to see. We can do this by writing a computed property in GenderOverview.vue that accesses the gender param of our route, like so:

...
computed: {
gender() {
return this.$route.params.gender
},
}
...

From there, we can make a dynamic page title that looks at the gender computed property and displays that string in a title case format:

computed: {
...,
pageTitle() {
return `${this.gender[0].toUpperCase()}${this.gender.slice(1)}`
},

The above approach uses template literals, string indexing, and the slice method to create a title case string, assuming the string is only 1 word. Feel free to write it differently so that multi-word titles are also properly formatted! Note: this time the slice method only has one argument, the starting index. When this happens, it copies the remainder of the string from the starting index to the end, which is what we want.

Add the pageTitle computed property to your template in place of the previous hardcoded title:

<template>
...
<h1 class="wrapper">{{ pageTitle }}</h1>
...
</template>

Test Your Knowledge: Quiz Question 4

Step 5: Add a productsByGender computed property and getter

For both the men’s and women’s pages, we’re going to need to filter all the products in the store by the appropriate gender. We could do this directly in the GenderOverview component, since right now no other component needs this type of functionality, but for practice purposes let’s leave this logic to a getter. And who knows, maybe we’ll create another feature in the future that does need this getter and we’ll already have the functionality in a more globally accessible place.

Computed properties vs getters: which one do I use and when? You can often answer this question by asking yourself: do other parts of my application need access to this information? If so, you should put that function in a more global place where all of them can find it, like a getter in store.js. If not, it’s fine to leave the logic/functionality where it is and wait until you have a reason to abstract it out into a higher level component or the store later.

Again though, we’re practicing getters here so that’s what we’ll do.

Inside GenderOverview.vue, create a generic productsByGender computed property that calls a getter by the same name. This particular getter should take the gender computed property as an argument. We pass in this.gender so the getter will know by which gender to filter our products.

computed: {
...,
productsByGender() {
return this.$store.getters.productsByGender(this.gender)
}
}

In store.js, add the getter to which you just referred. Because we’re passing an argument to this getter (the gender), there’s an extra-looking layer to this function. What’s happening is our getter is itself returning another function, which actually filters state.products by the gender we passed in:

getters: {
...,
productsByGender: (state) => (gender) => {
return state.products.filter(p => p.gender === gender)
},
productsByCategory: (category) => {
return this.state.products.filter(p => p.category === category)
}
}

Test Your Knowledge: Quiz Questions 5 and 6

Step 6: Add template markup and styles

Next, flesh out the GenderOverview.vue template and styles. Similar to the featuredProducts computed property in Home.vue, we’re looping through the productsByGender computed property on this page, displaying the each product’s image, name, and price, and linking to its respective detail page with a router-link:

Test Your Knowledge: Quiz Question 7

Step 7: Add the men’s page

Inside App.vue, add a <router-link> element for the men’s page after the women’s link:

<router-link
:to="{ name: 'gender-overview', params: { gender: 'men'}}" class="nav-items__item">Men</router-link>

All the functionality is already in place, so the men’s page should automatically look like this below:

Feedback: Did you like the addition of the quiz questions?

Tada! We just knocked out two new pages of our app with one component, each time using the same computed properties and getters. Well done!

Next up we’ll add a new widget to this component that allows the user to receive a set of random product recommendations. 👋

--

--

Nathan Magyar

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