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: add the JS Customer API to your theme
Make sure you’ve followed the instructions on how to install developer tools in order to include our lightweight API for your usage.
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.product_likes.value %}
<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
- Ensure you’re not logged in as a customer
- Visit the product page (or whever you put the
like-button
snippet) - The button should be disabled, showing you a notice to login
- Login as a customer
- Visit the previous page with the like button
- The button should say “Like”, and there should be no login notice
- Click the button, the text should change to “Liked”
- Refresh the page
- The button should still say “Liked”
- Click the button, the text should change back to “Like”
- Refresh the page
- The button should still say “Like”
- Like the product, then dislike it
- 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.product_likes.value %}
<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>