Now that we can successfully add products, let’s show them in the products listing page, which is managed in the ProductsList component in src/views/ProductsList.vue:

<template>
  <div class="">
    <h1>Products List</h1>
  </div>
</template>

Before we do anything else we must go to the Vuex store object and add a way to get the products list.

Products, as you recall, are stored through the addProduct mutation, which adds a product to the products array:

src/store.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    products: []
  },
  mutations: {
    addProduct(state, product) {
      state.products.push(product)
    }
  },
  actions: {}
})

We must add a getters object, which holds a products property:

export default new Vuex.Store({
  state: {
    products: []
  },
  mutations: {
    addProduct(state, product) {
      state.products.push(product)
    }
  },
  actions: {},
  getters: {
    products: state => state.products
  }
})

Now we can extract the products list by calling this.$store.getters.products in a component JavaScript, or use $store.getters.products in a component template.

So let’s go back to src/views/ProductsList.vue and we can iterate over those products:

<template>
  <div class="products-list">
    <div v-for="(product, index) in $store.getters.products" :key="index">
      <!-- ... -->
    </div>
  </div>
</template>

and for each product we display the information we have at our disposal:

<template>
  <div class="products-list">
    <div v-for="(product, index) in $store.getters.products" :key="index">
      <img :src="product.imageUrl" />
      <h2>{{product.name}}</h2>
      <p class="description">{{product.description}}</p>
      <p class="price">${{product.price}}</p>
    </div>
  </div>
</template>

We have a way to add products in the AddProduct component. Here we can add a way to remove a product, which we can use in our testing to avoid having lots of sample data and no way to remove a product.

Let’s first add a button, that once clicked calls the deleteProduct() method on the component, and passes it the product index:

<button @click="deleteProduct(index)">ⓧ</button>

Let’s show it in context:

<template>
  <div class="products-list">
    <div v-for="(product, index) in $store.getters.products" :key="index">
      <img :src="product.imageUrl" />
      <h2>{{product.name}}</h2>
      <p class="description">{{product.description}}</p>
      <p class="price">${{product.price}}</p>
      <button @click="deleteProduct(index)">ⓧ</button>
    </div>
  </div>
</template>

Here’s the deleteProduct method implementation:

<script>
export default {
  name: 'Products',
  methods: {
    deleteProduct(index) {
      this.$store.commit('deleteProduct', index)
    }
  }
}
</script>

we perform the deleteProduct mutation on the store, passing the product index.

Let’s add this mutation to src/store.js, right after the addProduct mutation:

mutations: {
  addProduct(state, product) {
    state.products.push(product)
  }
}

⬇️

mutations: {
  addProduct(state, product) {
    state.products.push(product)
  },
  deleteProduct(state, i) {
    state.products = state.products
      .slice(0, i)
      .concat(state.products.slice(i + 1, state.products.length))
  }
}

Now we can add and remove products as we want.

Let’s make the list render a bit nicer with this style:

<style scoped>
.products-list {
  display: flex;
  padding-top: 30px;
}
.products-list div {
  width: 33%;

  box-sizing: border-box;
  padding: 30px;
  background-color: lightsalmon;
}

button {
  padding: 30px;
  font-size: 2rem;
}

.description,
.price {
  padding-top: 20px;
}
</style>

Here’s our result:

You can find the working code at https://codesandbox.io/s/v5652yv1l


Go to the next lesson