Events and adding to cart

You've done some amazing work getting to this point! You've created your own Product component and we're now rendering products on the page dynamically. You also know what methods and computed properties are and how they work. Now it's time to take a look at events and specifically, how we can emit an event to add a product to the cart.

Learning Objectives

Learn how events work

You'll notice that we've got the 'Full details' and the 'Add to cart' buttons, but neither of them work because we haven't wired them up. Let's rectify that and wire up the Add to cart button so it adds the Product IDs to the cart.

Step 1

In your Product component, modify the button as follows:

<button v-on:click="addToCart(product)">Add to cart</button>

The v-on tells Vue to add an event listener to the button and to run the addToCart method on click. If you're familiar with JavaScript events, you'll appreciate that this is a nice efficiency gain as you no longer need to write and manage events yourself.

Step 2

Add a new method to the methods object in the Product component to handle the click:

// Product component

addToCart(product) {
  console.log(product);
}

Refresh your page and click one of the Add to cart buttons. You should see the product logged to the console.

Problem solving

Now we have a problem to solve. We need to push the product's ID into the cart array, but the cart exists in the data object that belongs to the app component. For security and data integrity reasons, we can't directly access it via the Product component. There are a number of ways to solve this and two examples are:

Using Vuex at this point is a little overkill as there are only two components. For that reason, let's emit an event and solve the problem that way. We'll incorporate Vuex later when our app becomes more complex

Let's visualise the process in a diagram:

Module 1 - Emitting events

Step 3

In your index.html file, add a v-on line to your Product component render tag to register the 'add-to-cart' event with the Product component.

<product
  v-for="product in this.products"
  v-bind:key="product.productId"
  v-bind:product="product"
  v-on:add-to-cart="updateCart" // Register the 'add-to-cart' event
></product>

Step 4

Head back to your addToCart function in the Product component and add:

// Product component

addToCart(product) {
  product.addedToCart = !product.addedToCart;
  this.$emit('add-to-cart', product.productId);
}

Firstly we've change the addedToCart property on the product to be equal to the opposite of what it currently is. For example, if addedToCart is false, !product.addedToCart will return true because a false becomes true when used with the NOT operator.

In a similar fashion, if addedToCart is true, then !product.addedToCart will return false because the NOT operator will only return true if the variable is 'falsy' (ask your coach if this concept is confusing).

Next up, we emit the event back up to the app component using the event add-to-cart. The app component receives this signal, and fires the updateCart method, which receives the Product ID.

Step 5

Now in your app component, add the following method into the methods object:

// app component

updateCart(productId) {
  this.cart.push(productId);
  console.log(this.cart);
},

Notice here we simply push the ID into the cart, then log the cart array (you can remove the console log later). If all has gone well you should see the Product ID added to the cart and the cart value in the header incrementing.

We can now get our button to react to the changes by adding the following ternary operator:

<button v-on:click="addToCart(product)">
  {{ product.addedToCart ? "Remove from cart" : "Add to cart" }}
</button>

Assignment - Coding challenge

The problem we have now is that you can continue to click the button and the ID will get added to the cart array over and over again.

Additional resources