Link

Collect product likes

Estimated time to implement: 2 hours, difficulty: 8/10

Overview

In this example, we’ll demonstrate how to add a button that allows customers to like products.

We’ll store product likes in the customer’s data using a data column that has a key of product_likes and contains a list of product handles. If you don’t have this data column set up, then the code in this example will not work unless you update it with one that you do have.

This example is using the Debut theme.

Step one: render the snippet

Navigate to layout/theme.liquid in your theme editor, then add the following code inside the <head> tag:

{% render 'customer-fields', customer_api: true, version: '<Version number>' %}

Step two: create a snippet to be used on the product page

Create a snippet called like-button, then render it wherever you need. For this example, I’m going to put it above the social-sharing snippet.

<!-- ... -->

<div class="product-single__description rte">
  {{ product.description }}
</div>

{% render 'like-button' %}

{% if section.settings.show_share_buttons %}
  {% include 'social-sharing', share_title: product.title, share_permalink: product.url, share_image: product %}
{% endif %}

<!-- ... -->

Step three: add the like button

Inside the like-button snippet, add the following button markup:

Add whatever class names you need. This example uses button classes from the Debut theme. You may need to add extra styling for the buttons to fit well with your theme.

<div class="like-button" data-product-handle="{{ product.handle }}">
  {% unless customer %}
    <p>You need to <a href="/account/login">login</a> to like this product.</p>
  {% endunless %}

  <button class="btn" {% unless customer %}disabled{% endunless %}>Like</button>
</div>

Step four: ensure we have correct data

Add a <script> that checks to ensure the customer has the product_likes data:

<div class="like-button" data-product-handle="{{ product.handle }}">
  {% unless customer %}
    <p>You need to <a href="/account/login">login</a> to like this product.</p>
  {% endunless %}

  <button class="btn" {% unless customer %}disabled{% endunless %}>Like</button>
</div>

<script>
  CF.customerReady(function() {
    // Get the customer's likes
    var likes = CF.customer.get('product_likes');

    // If the likes are missing, set them to an empty list
    if (!likes) likes = CF.customer.set({ product_likes: [] }).product_likes;
  });
</script>

Step five: listen for button clicks

In the <script> you added, add some code that finds the button in the document, then uses addEventListener to handle click events:

<div class="like-button" data-product-handle="{{ product.handle }}">
  {% unless customer %}
    <p>You need to <a href="/account/login">login</a> to like this product.</p>
  {% endunless %}

  <button class="btn" {% unless customer %}disabled{% endunless %}>Like</button>
</div>

<script>
  CF.customerReady(function() {
    var likes = CF.customer.get('product_likes');
    if (!likes) likes = CF.customer.set({ product_likes: [] }).product_likes;

    // We want to scope this script to this product, in case the snippet is used more than once on a page.
    var $buttonContainer = document.querySelector('.like-button[data-product-handle="{{ product.handle }}"]');
    var productHandle = "{{ product.handle }}";

    // Find the button
    var $likeButton = $buttonContainer.querySelector('button');

    // Listen for click events
    $likeButton.addEventListener('click', function() {
      // Add the product handle to the customer's liked_products
      // and ensure it is not present in the customer's disliked_products
      // If the customer's liked_products already included the product handle,
      // remove it. 
    });
  });
</script>

Step six: implement “Like” functionality

“Unlike” if the product is already liked

Some of the snippet has been omitted for simplicity.

// ... $likeButton, likes, productHandle, other variables ...

$likeButton.addEventListener('click', function() {
  // Get the index of the product handle to see if it's already there
  var indexOfLikedProduct = likes.indexOf(productHandle);

  if (indexOfLikedProduct > -1) {
    // Already liked, so just remove it.
    likes.splice(indexOfLikedProduct, 1);
  }
});

Add the product handle to the customer’s product_likes

// ... $likeButton, likes, productHandle, other variables ...

$likeButton.addEventListener('click', function() {
  // Get the index of the product handle to see if it's already there
  var indexOfLikedProduct = likes.indexOf(productHandle);

  if (indexOfLikedProduct > -1) {
    // Already liked, so remove it
    likes.splice(indexOfLikedProduct, 1);
  } else {
    // Not liked yet, so add it
    likes.push(productHandle);
  }
});

Step seven: handle initial UI state

If a product is liked, the “Like” button shouldn’t still be telling the user to like the product. It should say “Liked”, informing the user they’ve liked the product.

Go back up to the button markup, make the following liquid changes to handle this:

{% assign likes = customer.metafields.customer_fields.data.product_likes %}

<div class="like-button" data-product-handle="{{ product.handle }}">
  {% unless customer %}
    <p>You need to <a href="/account/login">login</a> to like this product.</p>
  {% endunless %}

  <button class="btn" {% unless customer %}disabled{% endunless %}>
    {% if likes contains product.handle %}
      Liked
    {% else %}
      Like
    {% endif %}
  </button>
</div>

Now the on page load, the correct text will display the state of the product in regards to the customer’s liked products.

Step eight: handle changing UI state

You’ll want the button text to change when the user clicks it. You’ll need to add some more logic to your click event handlers to change the innerText of the button:

$likeButton.addEventListener('click', function() {
  // Get the index of the product handle to see if it's already there
  var indexOfLikedProduct = likes.indexOf(productHandle);

  if (indexOfLikedProduct > -1) {
    // Already liked, so remove it
    likes.splice(indexOfLikedProduct, 1);
    $likeButton.innerText = 'Like';
  } else {
    // Not liked yet, so add it
    likes.push(productHandle);
    $likeButton.innerText = 'Liked';
  }

  // Update the customer data
  CF.customer.update({ product_likes: likes });
});

Step nine: test it out

  1. Ensure you’re not logged in as a customer
  2. Visit the product page (or whever you put the like-button snippet)
  3. The button should be disabled, showing you a notice to login
  4. Login as a customer
  5. Visit the previous page with the like button
  6. The button should say “Like”, and there should be no login notice
  7. Click the button, the text should change to “Liked”
  8. Refresh the page
  9. The button should still say “Liked”
  10. Click the button, the text should change back to “Like”
  11. Refresh the page
  12. The button should still say “Like”
  13. Like the product, then dislike it
  14. Refresh the page and the button should say “Like”

You’re done! Congratulations on implementing a cool feature without needing another app! 😎

End result

Your snippet should look something like this:

{% assign likes = customer.metafields.customer_fields.data.product_likes %}

<div class="like-button" data-product-handle="{{ product.handle }}">
  {% unless customer %}
    <p>You need to <a href="/account/login">login</a> to like this product.</p>
  {% endunless %}

  <button class="btn" {% unless customer %}disabled{% endunless %}>
    {% if likes contains product.handle %}
      Liked
    {% else %}
      Like
    {% endif %}
  </button>
</div>

<script>
  CF.customerReady(function() {
    var likes = CF.customer.get('product_likes');
    if (!likes) likes = CF.customer.set({ product_likes: [] }).product_likes;

    // We want to scope this script to this product, in case the snippet is used more than once on a page.
    var $buttonContainer = document.querySelector('.like-button[data-product-handle="{{ product.handle }}"]');
    var productHandle = "{{ product.handle }}";

    // Find the button
    var $likeButton = $buttonContainer.querySelector('button');

    // Listen for click events
    $likeButton.addEventListener('click', function() {
      // Get the index of the product handle to see if it's already there
      var indexOfLikedProduct = likes.indexOf(productHandle);

      if (indexOfLikedProduct > -1) {
        // Already liked, so remove it
        likes.splice(indexOfLikedProduct, 1);
        $likeButton.innerText = 'Like';
      } else {
        // Not liked yet, so add it
        likes.push(productHandle);
        $likeButton.innerText = 'Liked';
      }

      // Update the customer data
      CF.customer.update({ product_likes: likes });
    });
  });
</script>