Reputation: 418
I'm trying to build on caroline's solution for related products with metafields to build related product variants. I.e when you click on the white color variant for a desk, you will see the white variant of a chair as a related product. (As opposed to linking the desk product to the chair product regardless of variant.) Caroline's solution is here: https://gist.github.com/carolineschnapp/1003334 and below is my code. Right now it's putting the same product twice on page load, and nothing happens when a different vairant is selected. The way I am formatting the metafield value for each variant is by putting "related-product-handle-1, variant-id-1, related-product-handle-2,variant-id-2, related-product-handle-3, variant-id-3" instead of just the product handles.
{% assign image_size = 'compact' %}
{% assign heading = 'Related Products' %}
{% if product.selected_or_first_available_variant.metafields.recommendations.productHandles %}
<h3>{{ heading }}</h3>
<ul class="related-products"></ul>
{% endif %}
<script>!window.jQuery && document.write('<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"><\/script>')</script>
{{ 'api.jquery.js' | shopify_asset_url | script_tag }}
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
setTimeout(function() {
var dd = $('.single-option-selector#product-select-option-0');
var vId = location.search.substring(9);
switchRelated(vId);
dd.on('change', function() {
$('ul.related-products').empty();
var vId = location.search.substring(9);
switchRelated(vId);
});
function switchRelated(vId) {
var list = $('ul.related-products');
var vIdd = parseInt(vId);
{% for variant in product.variants %}
{% if variantId == vIdd %}
{% if variant.metafields.recommendations.productHandles %}
recommendations = jQuery.trim({{ variant.metafields.recommendations.productHandles | json }}).split(/[\s,;]+/);
for (var i=0; i < (recommendations.length); i+=2 ) {
var j = (i + 1);
if (recommendations.length && recommendations[i] && recommendations[j] !== '') {
jQuery.getJSON('/products/' + recommendations[i] + '.js', function(product) {
product.variants.forEach(function(variant) {
if (variant.id == parseInt(recommendations[j])) {
list.append('<li><div class="image"><a href="' + product.url + '?variant=' + recommendations[j] +'"><img src="' + variant.featured_image.src + '" /></a></div><h4><a href="' + product.url + '?variant=' + recommendations[j] + '">' + product.title + '</a></h4></li>');
}
});
});
}
}
{% endif %}
{% endif %}
{% endfor %}
}
}, 1);
});
</script>
Answer edited: The first was extremely helpful once I made some corrections to syntax errors and a couple of short additions. Here is my edited version of the answer for anyone who may need it:
Product.liquid:
{% for variant in product.variants %}
{% capture metafield_data %}{% endcapture %}
{% assign related_products = variant.metafields.recommendations.productHandles | split: '|' %}
{% for related_product in related_products %}
{% assign metafield_items = related_product | split: ',' %}
{% assign r_p = metafield_items[0] %}
{% assign r_v = metafield_items[1] | plus: 0 %}
{% assign r_n = all_products[r_p].title %}
{% for related_variant in all_products[r_p].variants %}
{% if related_variant.id == r_v %}
{% assign r_i = related_variant.image.src | img_url: 'small' %}
{% endif %}
{% endfor %}
{% capture metafield_data %}{{metafield_data}}{{ r_p }},{{ r_v }},{{ r_i }},{{ r_n }}{% unless forloop.last %}|{% endunless %}{% endcapture %}
{% endfor %}
<option id="{{ variant.id }}" data-metafield="{{ metafield_data }}" {% if variant == product.selected_or_first_available_variant %} selected="selected" {% endif %} value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}- {{ related_products.size }}</option>
{% endfor %}
And related-variants javascript snippet:
$(document).ready(function(){
setTimeout(function() {
var dd = $('.single-option-selector#product-select-option-0');
if(location.search.substring(9) != ''){
var vId = location.search.substring(9);
}
else {
var vId = {{ product.selected_or_first_available_variant.id }};
}
switchRelated(vId);
$('#product-select option').each(function(index, element){
$(".single-option-selector#product-select-option-0 option:eq(" + index + ")").attr('id', element.id);
$('.single-option-selector#product-select-option-0 option:eq(' + index + ')').attr('data-metafield', $(element).attr("data-metafield"));
$('#product-select option:eq(' + index + ')').attr('id', '');
});
dd.on('change', function() {
var list = $('ul.related-products');
$(list).children().remove();
$(list).empty();
if(location.search.substring(9) != ''){
var vId = location.search.substring(9);
}
else {
var vId = {{ product.selected_or_first_available_variant.id }};
}
switchRelated(vId);
});
function switchRelated(vId) {
var list = $('ul.related-products');
$(list).children().remove();
$(list).empty();
var vIdd = parseInt(vId);
console.log(vIdd)
var variant_matches = $('#' + vId).attr('data-metafield').split('|');
for (var i=0; i < variant_matches.length; i++) {
var items = variant_matches[i].split(',');
list.append('<li><div class="image"><a href="/products/'+items[0]+'/?variant='+items[1]+'"><img src="'+items[2]+'" /></a></div><h4><a href="/products/' + items[0] + '?variant=' + items[2] + '">' + items[3].replace('_','') + '</a></h4></li>');
}
}
}, 1);
});
The only thing I'm nervous about is the fact that I am copying over the data from the 'product-select' dropdown over to the 'single-option-selector' dropdown. I am doing this because there is no template for rendering the single-option-selector, it seems to be getting added through javascript. If anyone has any insight into manipulating the single-option-selector in liquid, please let me know. Thank you!!
Upvotes: 2
Views: 4554
Reputation: 2925
The fastest way to implement this is to let Shopify servers structure the elements for to pull links and image URLs with any AJAX calls. Here's how to do it.
From your example of productHandles, I'm guessing the 3 items in the list are related to a particular variant id xxxxxx
. Structure the metafield value this way
rph-1,rph-v-id-1|rph-2,rph-v-id-2|rph-3,rph-v-id-3
Now in product liquid find this section
<select class="product-select" id="product-select" name="id" .... </select>
Change the inside html to the one below -
{% for variant in product.variants %}
{% assign related_products = variant.metafields.recommendations.productHandles | split: '|' %}
{% for related_product in related_products %}
{% assign metafield_items = related_product | split: ',' %}
{% assign r_p = metafield_items[0] %}
{% assign r_v = metafield_items[1] | plus: 0 %} {% comment %} converting string to number {% endcomment %}
{% assign r_n = all_products[r_p].title | replace: ' ','_' %}
{% for related_variant in all_products[r_p].variants %}
{% if related_variant.id == r_v %} {% comment %} fails if r_v is a string {% endcomment %}
{% assign r_i = related_variant.image.src }}
{% endif %}
{% endfor %}
{% capture metafield_data %}{{metafield_data}}{{ r_p }},{{ r_v }},{{ r_i }},{{ r_n }}{% unless forloop.last %}|{% endunless %}{% endcapture %}
{% endfor %}
<option id="{{ variant.id }}" metafield-data={{ metafield_data }}{% if variant == product.selected_or_first_available_variant %} selected="selected" {% endif %} value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
"metafield_data" will contain all the related products information (product,variant,variant image).
JS to replace "switchRelated(vId)"
function switchRelated(vId) {
var list = $('ul.related-products');
var vIdd = parseInt(vId);
list.children().remove()
var variant_matches = $('#vId').attr('metafield-data').split('|')
for (var i=0; i < variant_matches.length; i++) {
var items = variant_matches[i].split(',')
list.append('<li><div class="image"><a href="/products/'+items[0]+'/?variant='+items[1]+'"><img src="'+items[2]+'" /></a></div><h4><a href="/products/'+items[0]+'?variant='item[2]'">'+items[3].replace('_','')+'</a></h4></li>');
}
}
In layman terms, you are taking the product handle and variant id from the metafields and adding title and image to them using liquid (server side function). You are then matching them to variants and assigning a data variable in the element which you are using again to change the html elements.
P.S. The code is long and not properly aligned and I may have missed code punctuations here and there. Please check them. The logic is simple and the entire weight of AJAX is removed and shifted to normal HTML calls.
Upvotes: 1