What's your biggest current challenge? Have your say in Community Polls along the right column.
Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Bug when metafield is type Page Reference and Accept list of values

Bug when metafield is type Page Reference and Accept list of values

samgoodcity
Shopify Partner
10 0 6

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

Replies 11 (11)

MetafieldsGuru
Shopify Partner
160 32 103

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.

Check out Metafields Guru, the #1 ranked metafields app.

Bulk editor | Data import/export | Metafield sets | Browser extension
samgoodcity
Shopify Partner
10 0 6

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.

 

samgoodcity_1-1653311034404.png

 

 

Hopefully it's something Shopify are working on, but would be nice to get some information directly.

 

MetafieldsGuru
Shopify Partner
160 32 103

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 %}

 

Check out Metafields Guru, the #1 ranked metafields app.

Bulk editor | Data import/export | Metafield sets | Browser extension
samgoodcity
Shopify Partner
10 0 6

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.

samgoodcity
Shopify Partner
10 0 6

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 😉

MetafieldsGuru
Shopify Partner
160 32 103

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:

MetafieldsGuru_0-1653314636466.png

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.

Check out Metafields Guru, the #1 ranked metafields app.

Bulk editor | Data import/export | Metafield sets | Browser extension
samgoodcity
Shopify Partner
10 0 6

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

MetafieldsGuru
Shopify Partner
160 32 103

Got it! You're right, in this case the sort order might be messed up.

Check out Metafields Guru, the #1 ranked metafields app.

Bulk editor | Data import/export | Metafield sets | Browser extension
samgoodcity
Shopify Partner
10 0 6

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.

marvinhuebner
Shopify Partner
3 0 1

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>

 

ThomasBorowski
Shopify Partner
803 71 242

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:

 

  • Split up the "gid://shopify..." string into an array that holds all the page IDs
  • Use the global pages object and the where filter to get the page object for each ID

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>

 

★ Smart Upgrades, Tips and Tutorials for Shopify themes: cartpunk.com
Did my solution work? Help other Community members easily find the correct solution and apply it to their own stores by marking it as the Accepted Solution and giving it a Thumbs Up