When a product in the list is clicked, we want to open the single product page.

How do we do so?

We add a click handler to the container div that holds the product card in the list:

<template>
  <div class="products-list">
    <div v-for="(product, index) in $store.getters.products" :key="index" @click="$router.push(`/product/${product.slug}`)">
      <!--... -->
    </div>
  </div>
</template>

See, the click handler is inline in the template, instead of being a method. We call

$router.push(`/product/${product.slug}`)

to change the URL, which also changes the component that is responsible for that URL, based on the router configuration which we did in src/router.js.

We also need to do one thing here. Inside this div we have the button we use to remove a product:

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

we must add the stop event modifier to automatically call stopPropagation on the event, otherwise Vue first calls deleteProduct, and then executes the $router.push call:

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

That’s it. Now we can go in the src/views/SingleProduct.vue file.

It contains this code now:

<template>
  <div class="">
    <h1>Single Product</h1>
  </div>
</template>

We must show the product details. Where do we get them? From the route. In particular, we lookup the slug in the products array when the components are first initialized, in the mounted life cycle hook, and we assign it to the product component data:

<script>
export default {
  name: 'Product',
  data() {
    return {
      product: {}
    }
  },
  mounted() {
    this.product = this.$store.getters.products
      .filter(item => item.slug === this.$route.params.slug)
      .shift()
  }
}
</script>

Notice how we get the products array from Vuex using this.$store.getters.products, and we use the filter() method on the array to just get the item that matches the slug. shift() at the end just returns the first item in the array.

Once this is done, we can show the product data in the template:

<template>
  <div class="container">
    <div class="content">
      <h1>{{product.name}}</h1>
      <p class="description">{{product.description}}</p>
      <p class="price">${{product.price}}</p>
    </div>
    <img class="image" :src="product.imageUrl" />
  </div>
</template>

and we style it a bit:


<style scoped>
.container {
  display: grid;
  grid-template-columns: 50% 50%;
  padding-top: 50px;
  margin: 0 auto;
  max-width: 1200px;
}
.image {
}

.content h1 {
  padding-bottom: 50px;
}

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

The full source code at this point is available at https://codesandbox.io/s/52q3zml55p


Go to the next lesson