Hello,
I’m trying to write a custom hits display widget that will cycle through all Shopify products that have a colour option and will then make a call to the Shopify API to retrieve the additional product variants. However, since this is an async function, the UI ends up displaying [object Promise] for each hit instead of rendering the widget. My code is as follows:
import algoliasearch, { SearchClient } from 'algoliasearch/lite';
import instantsearch from 'instantsearch.js';
import { connectSortBy, connectHits } from 'instantsearch.js/es/connectors';
import {
stats,
toggleRefinement,
rangeSlider,
refinementList,
pagination,
} from 'instantsearch.js/es/widgets';
import { formatAsMoney } from '../../modules/utils';
const searchClient = algoliasearch(
'appID',
'appKey'
);
const renderHits = (renderOptions, isFirstRender) => {
const { hits, widgetParams } = renderOptions;
let innerHtml = hits.map(async (hit) => {
let images: any[] = [];
images.push({ url: hit.product_image, class: 'primary' });
let imageHTML = images.map((image) => {
return `<img
class="product-card__img product-card__img--${image.class} lazyload"
data-src="${image.url}"
/>`;
});
// variants should only show for colour options, not size etc.
// We're showing 3, and if there are more than that the plus button shows
let variantsHTML = '';
// Check if there are colour or material variants for this item
// If there is also a size available, don't show the colour picker
if (
(hit.option_names.includes('color') ||
hit.option_names.includes('colour') ||
hit.option_names.includes('material')) &&
!hit.option_names.includes('size')
) {
// Make a call to get the variants
const { html } = await fetch(`/api/shop/variant-colours/${hit.id}`).then(
(r) => r.json()
);
// Build the HTML
variantsHTML = `<div role="group" aria-labelledby="option-label_colour" class="variant-selectors variant-selectors--mini">
<span class="sr-only" id="option-label__colour">Select a Colour</span>
<div class="variant-selectors__buttons">
${html}
<span class="variant-selectors__additional"></span>
</div>
</div>`;
}
return `
<li class="collection__grid-item">
<div class="product-card js-product" data-product="${hit.handle}">
<a href="/products/${hit.handle}" class="product-card__images"
>${imageHTML}</a
>
<div class="product-card__details">
<h3 class="product-card__title">${hit.title}</h3>
<p class="product-card__price js-product-price">
$${hit.price.toLocaleString('en-US')}
</p>
</div>
<div class="product-card__btns">
<button class="bag-btn product-card__add-to-cart js-add-to-cart" data-id="gid://shopify/ProductVariant/${
hit.objectID
}">
<span class="sr-only">Add to Cart</span>
</button>
${variantsHTML}
</div>
</div>
</li>
`;
});
widgetParams.container.innerHTML = `<ol class="collection__grid">${innerHtml.join(
''
)}</ol>`;
};
const customHits = connectHits(renderHits);
function init(searchClient: SearchClient) {
const search = instantsearch({
indexName: 'shopify_products',
routing: true,
searchClient,
});
document.addEventListener('DOMContentLoaded', () => {
search.addWidgets([
customHits({
container: document.querySelector('#hits'),
}),
]);
});
search.start();
}
init(searchClient);
This is being built using the AdonisJS framework. Any help on what I’m missing would be appreciated. Thanks!