A space to discuss online store customization, theme development, and Liquid templating.
I'm using the new metafields interface. I've created a field called "child_pages" on Pages with type page_reference and accepting a list of values. I'm trying to output the contents of the field in a section.
{{ page.metafields.my_fields.child_pages }} is returning a list represented as a string as as I expect:
["gid://shopify/OnlineStorePage/89456738486",...]
{{ page.metafields.my_fields.child_pages.type }} is returning list.page_reference again as I expect.
But {{ page.metafields.my_fields.child_pages.value }} is returning nothing.
Also when I try to loop through the list items using both:
{% for child_page in page.metafields.my_fields.child_pages %}
{{ child_page.title }}
{% endfor %}
and:
{% for child_page in page.metafields.my_fields.child_pages.value %}
{{ child_page.title }}
{% endfor %}
Nothing is output, obviously in the second case as it's empty.
This does not happen with metafields with the type product_reference.
{{ page.metafields.my_fields.related_products.value }} returns ProductListDrop and I'm able to loop through the values using: {% for related_product in page.metafields.my_fields.related_products.value %}
Pretty sure this is a bug on the Shopify side. Am I missing something?
Cheers,
Sam
Hi @samgoodcity ,
Indeed, looping through the list of pages doesn't work. But I don't think that it's a bug. If you refer to the official documentation, you'll notice that only two "list" metafield types are supported as for now:
1) list.single_line_text_field
2) list.product_reference
It seems like your idea is slightly ahead of its time and now you need to wait for Shopify to make Liquid support the lists of pages.
Hey MetafieldsGuru, thanks for your response.
I can kinda see what you mean, but if that were the case it would mean that it's impossible to obtain the value from a metafield where the settings are Page reference and Accepts a list of values as below. I could understand it if the list input was disabled when selecting Page as the reference type.. But as of now it really feels like a bug.
Hopefully it's something Shopify are working on, but would be nice to get some information directly.
Actually, it's possible to loop through the list of pages with the current functionality if you apply a slightly different logic. I was curious to see if there's a workaround and managed to find it. But it's an absolutely disgusting and inefficient solution and I strongly recommend not to use it for purposes other than testing/playing around/learning the full power of Liquid (a note for the future readers).
Currently one can access the list of the pages as a JSON array and not a Liquid array. As you've mentioned in your initial message, Liquid is not compatible with JSON and treats it like a string. The idea is to obtain the JSON array of the related pages from a metafield and clean it up with a bunch of Liquid filters to leave the IDs only. Next, you'll be able to paginate though all the pages in your store and check if their IDs belong to the list we've prepared earlier. If the list contains the ID of a page, you can display its title/content. If it doesn't, just ignore that page.
Here's the code:
{% assign related_pages_ids = product.metafields.my_fields.list_of_pages | replace: '"', "" | replace: "[", "" | replace: "]", "" | replace: "gid://shopify/OnlineStorePage/" , "" %}
{% paginate pages by 250 %}
{% for generic_page in pages %}
{% if related_pages_ids contains generic_page.id %}
<p> {{ generic_page.title }} </p>
{% endif %}
{% endfor %}
{{ paginate | default_pagination: next: 'Older', previous: 'Newer' }}
{% endpaginate %}
Hahah yeah I had figured we might be able to do some hacking in liquid to isolate the ids and get the pages that way, so I think that solution could work for us. But totally agree that it's inefficient and less than ideal.
But really appreciate the approach and your code. Hopefully we can get a long-term solution.
Just realised that the down side of this code is that we lose the ordering of the IDs that we would otherwise have. But it's something at least 😉
The sort order of the IDs doesn't change when you remove the rest of the JSON. Here's a screenshot showing the output for the standard and cleaned up versions of the list:
If you look closer at the IDs in both lists, you'll notice that the sort order is the same.
Also, in terms of performance, it might be slightly less of a disaster if you create some sort of a global variable to store the cached list of the pages instead of retrieving a fresh copy each time a product page is refreshed.
Agree the order of the IDs remains the same in the list, but the order in which the pages will be displayed is when each page appears in the pagination loop, which doesn't necessarily match the order they appear in the "list".
We can find ID index 2 before ID index 1 for example.
One of the nice things about the metafield list of pages references is the drag and drop reordering of pages, which is why I was drawn to the idea in the first place.
Maybe we could go totally crazy and loop through all the paginated pages for each id in the list until we find it. But then that's making an inefficient solution n times worse lol
Got it! You're right, in this case the sort order might be messed up.
For anyone interested, I didn't end up using the solution above and for the time being, until you can loop through a page list on a metafield, I ended up using a linklist.
Rather than collecting the list of pages inside a metafield and looping over all the pages until you find the right one. I opted for looping over the pages inside a linklist, which has the benefits of:
- Being efficient
- Having native ordering
This largely works because you can access the link.object property, which gives you access to all of the fields of the object you are linking to (page/product/whatever).
So yeah, that's my solution until this bug is fixed.
I had exactly the same problem today, in my case with a product list.
The solution here absolutely not the prettiest, but apparently currently not otherwise solvable (unfortunately).
Here is the code if someone needs something similar:
<!-- Get comma seperated list of ids -->
{% assign product_color_variant_ids = product.metafields.color_variants.products | replace: '"', "" | replace: "[", "" | replace: "]", "" | replace: "gid://shopify/Product/" , "" %}
<!-- Convert string with ids to array to loop through -->
{% assign product_color_variant_id_list = product_color_variant_ids | split: "," %}
<!-- Loop through ids -->
<ul>
{% for product_variant in product_color_variant_id_list %}
<!-- Convert product id from string to int for usage in switch case -->
{% assign product_variant_id = product_variant | plus: 0 %}
<!-- Loop through all products (not very nice for the performance, but unfortunately there is no other way) -->
{% for product_item in collections.all.products %}
{% case product_item.id %}
{% when product_variant_id %}
{% assign product_variant_data = product_item %}
<li>
<a href="{{ product_variant_data.url }}">
{{ product_variant_data.title }}
</a>
</li>
{% endcase %}
{% endfor %}
{% endfor %}
</ul>
This does indeed seem like a bug. It appears that while a metafield of type list.product_reference returns a ProductListDrop for its value property, the value of a metafield of type list.page_reference is empty.
I ended up using this workaround:
Here's the code to create a list of pages assigned to a customer via a metafield of type list.page_reference with the namespace custom and the key pages:
{% assign the_pages = customer.metafields.custom.pages | replace: "[", "" | replace: "]", "" | remove: '"' | remove: 'gid://shopify/OnlineStorePage/' | split: "," %}
<ul>
{% for the_page in the_pages %}
{% assign page_id = the_page | times: 1 %}
{% assign page_obj = pages | where: 'id', page_id | first %}
<li><a href="{{page_obj.url}}">{{ page_obj.title }}</a></li>
{% endfor %}
</ul>