Conversations about creating, managing, and using metafields to store and retrieve custom data for apps and themes.
Hello! Made a new code page- selections/avatar-carousel.liquid. It uses product metafield to fetch information. For some reason it is not working and get the error metafield null, like there is no metafield at all. I tried to rewrite the code in different ways to see the issue as well as checked the custom metafield Product Avatars but it's not working.
I am adding how the code looks currently.
Also the new metafield is Product metafield definition, Name: Avatar Products, Namespace and Key: custom.avatar_products, Type: Multi Line but before it was JSON otherwise same.
Avatars are saved as avatar1,avatar2, avatar3... avatar10
Can you please see why it won't fetch information?
{% comment %}
Avatar Carousel with Metafield-based Product Association
{% endcomment %}
<div class="avatar-carousel">
<div class="loading-indicator">Loading...</div>
<div class="avatar-container">
{% for i in (1..10) %}
{% assign avatar_products = product.metafields.custom.avatar_products | default: "" | split: "," %}
<div class="avatar-slide {% if forloop.index == 2 %}active{% elsif forloop.index == 1 %}prev{% elsif forloop.index == 3 %}next{% endif %}" data-index="{{ forloop.index }}">
<img src="{{ 'avatar' | append: i | append: '.png' | asset_url }}" alt="Avatar {{ i }}" class="avatar-image">
<div class="product-labels">
{% for product_handle in avatar_products %}
{% assign product = all_products[product_handle] %}
{% if product != blank %}
<div class="product-label" data-product-id="{{ product.id }}">
<div class="product-name">{{ product.title }}</div>
<a href="{{ product.url }}" class="product-info-link">View Product</a>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="outfit-details-text">
Click the avatar to view outfit details.
</div>
<button class="nav-button prev" aria-label="Previous avatar"><</button>
<button class="nav-button next" aria-label="Next avatar">></button>
</div>
{% schema %}
{
"name": "Avatar Carousel",
"settings": []
}
{% endschema %}
<style>
.outfit-details-text {
position: absolute;
bottom: 1vw;
left: 50%;
transform: translateX(-50%);
width: 30%;
text-align: center;
padding: 0.5em 1em;
background-color: rgba(245, 245, 245, 0.9);
color: #A9A9A9;
font-size: clamp(10px, 2vw, 14px);
border-radius: 25px;
font-family: Arial, sans-serif;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 768px) {
.outfit-details-text {
bottom: 3%;
width: 80%;
font-size: clamp(8px, 3vw, 12px);
padding: 0.7em 1em;
white-space: normal;
max-height: 4.2em;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: clip;
line-height: 1.4;
}
}
.avatar-carousel {
position: relative;
width: 100%;
height: 80vh;
margin: 0 auto;
overflow: hidden;
background: radial-gradient(circle, #ffffff 0%, #f5f5f5 60%, #e0e0e0 80%);
border-radius: 0px;
box-shadow: 0 40px 100px rgba(0, 0, 0, 0.5),
0 20px 50px rgba(0, 0, 0, 0.3) inset;
}
.avatar-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.avatar-slide {
position: absolute;
width: 30%;
height: 80%;
top: 45%;
left: 50%;
transform: translate(-50%, -50%);
transition: all 0.3s ease;
cursor: pointer;
opacity: 0.5;
filter: blur(5px);
}
.avatar-slide.prev,
.avatar-slide.next {
width: 25.5%;
height: 68%;
}
.avatar-slide.prev {
left: 20%;
}
.avatar-slide.active {
opacity: 1;
filter: none;
z-index: 2;
}
.avatar-slide.next {
right: 20%;
left: auto;
}
.avatar-slide.active.expanded {
transform: translate(-50%, -50%) scale(1.1);
}
@media (max-width: 768px) {
.avatar-slide {
width: 60%;
height: 65%;
top: 50%;
}
.avatar-slide.prev,
.avatar-slide.next {
width: 51%;
height: 55.25%;
}
.avatar-slide.prev {
left: 10%;
}
.avatar-slide.next {
right: 10%;
left: auto;
}
}
.avatar-image {
width: 100%;
height: 100%;
object-fit: contain;
}
.nav-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
font-size: 24px;
padding: 15px;
cursor: pointer;
z-index: 10;
}
.nav-button.prev {
left: 20px;
}
.nav-button.next {
right: 20px;
}
.loading-indicator {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 20px;
z-index: 5;
}
.product-labels {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
}
.product-label {
position: absolute;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
font-size: 14px;
text-align: center;
}
.product-name {
font-weight: bold;
margin-bottom: 5px;
}
.product-info-link {
display: inline-block;
color: #007bff;
text-decoration: underline;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const carousel = document.querySelector('.avatar-carousel');
const slides = carousel.querySelectorAll('.avatar-slide');
const prevButton = carousel.querySelector('.nav-button.prev');
const nextButton = carousel.querySelector('.nav-button.next');
const loadingIndicator = carousel.querySelector('.loading-indicator');
let currentSlide = 1;
function updateSlides() {
slides.forEach((slide, index) => {
slide.classList.remove('prev', 'active', 'next', 'expanded');
slide.style.opacity = '0.5';
slide.style.filter = 'blur(5px)';
if (index === currentSlide) {
slide.classList.add('active');
slide.style.opacity = '1';
slide.style.filter = 'none';
} else if (index === (currentSlide - 1 + slides.length) % slides.length) {
slide.classList.add('prev');
} else if (index === (currentSlide + 1) % slides.length) {
slide.classList.add('next');
} else {
slide.style.display = 'none';
}
});
slides[currentSlide].style.display = '';
slides[(currentSlide - 1 + slides.length) % slides.length].style.display = '';
slides[(currentSlide + 1) % slides.length].style.display = '';
}
function showNextSlide() {
currentSlide = (currentSlide + 1) % slides.length;
updateSlides();
}
function showPrevSlide() {
currentSlide = (currentSlide - 1 + slides.length) % slides.length;
updateSlides();
}
function toggleExpand(slide) {
if (slide.classList.contains('active')) {
slide.classList.toggle('expanded');
const productLabels = slide.querySelector('.product-labels');
productLabels.style.display = slide.classList.contains('expanded') ? 'block' : 'none';
slides.forEach(s => {
if (s !== slide) {
s.style.opacity = slide.classList.contains('expanded') ? '0.2' : '0.5';
}
});
}
}
prevButton.addEventListener('click', showPrevSlide);
nextButton.addEventListener('click', showNextSlide);
// Event listeners for side avatar clicks
slides.forEach(slide => {
slide.addEventListener('click', () => {
if (slide.classList.contains('prev')) {
showPrevSlide();
} else if (slide.classList.contains('next')) {
showNextSlide();
} else {
toggleExpand(slide);
}
});
});
// Keyboard navigation (left and right arrows)
document.addEventListener('keydown', (event) => {
if (event.key === 'ArrowLeft') {
showPrevSlide();
} else if (event.key === 'ArrowRight') {
showNextSlide();
}
});
// Hide loading indicator when all images are loaded
Promise.all(Array.from(carousel.querySelectorAll('img')).map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => { img.onload = img.onerror = resolve; });
})).then(() => {
loadingIndicator.style.display = 'none';
updateSlides();
});
});
</script>