Using AJAX to get a filtered collection.products object

Our store uses the Dawn theme (15.4). When a visitor views our collection page and selects some filters, a request URL is used to fetch an updated product grid. I see that the facets.js script uses this URL. It looks something like this:

/collections/all-paintings?section_id=template--22970116243737__product_grid_KMJTNw&filter.v.availability=1&filter.p.m.custom.medium=Acrylic+on+Canvas&sort_by=title-ascending

I’d like to make my own AJAX request using that URL to return a JSON object containing that same updated list of products without any HTML markup. Is there a way I can do this? The liquid file where that section in that URL is the main-collection-product-grid.liquid, so that page is already processing the filtered collection.products anyway. Can I insert some code on that page that will produce a JSON object that I can return to my AJAX script, or that I can hide somewhere where I can extract it from the output? I will then be storing that data in sessionStorage or something like that so that it only get’s retrieved on the initial loading of the new filtered set of products, not on successive pages.

Any help would be appreciated.

Zeb

2 Likes

Hey @ZebEllis

Shopify doesn’t return JSON from those filter URLs by default. What you’ll need to do is modify your main-collection-product-grid.liquid section to detect when a request is asking for JSON (for example, by checking a query param like ?view=json), and if so, output just the product data in JSON format using Liquid. Once that’s set up, your AJAX can hit something like:

/collections/all-paintings?section_id=...&filter...&view=json

Then store the result in sessionStorage like you planned.

Best,
Moeed

This is Section rendering API (Section Rendering API) combined with storefront filtering (Storefront filtering)

The already mentioned ?view=json parameter allows you to select alternate template (Alternate templates)

One benefit of using alternate templates is that you can specify "layout": none which will allow to extend limits on pagination and to not render headers and footers (Liquid tags: layout) – great for producing JSON files.

In liquid code you can check for template suffix to determine if alternate template is requested (Liquid objects: template)

Thank you both for pointing me in the right direction. I feel like I’m so close, but I’ve been getting unexpected results. Here’s what I’ve done…

I set up an alternative template for collections called collections.output.liquid. So “output” is what I use in the “view” url parameter: &view=output. The alternative template page has layout none at the top, and I use liquid to process the filtered collection.products that is already available without needing to do much. So I just serialize the filtered collection.products object and echo it. When I manually point a browser tab to the url with the filter and sort parameters:

/collections/all-paintings?filter.v.availability=1&sort_by=title-ascending&view=output

just to test it, I get exactly what I expect: a string matching the serialized object I echoed. It just fills the browser window. That seemed like a good start. All the products and their details are there. Great… except that when I use the same URL in an AJAX request, I get an object containing some data about a single collection, no products. It’s not even possible for my liquid code to produce this output. So something odd is happening here.

Here’s the rest of the details on how I set everything up:

On my regular collections.json template is the main-collection-product-grid.liquid section. I have inserted a style tag linking to a custom JS asset (deferred), and in that document is the following code:

function AA_fetchFilteredProducts()
    {
    const pathname = window.location.pathname;
    let queryStr = window.location.href.split('?')[1];
    
    if ( queryStr )
        {
        queryStr += ( queryStr.includes('view=output') ) ? '' : '&view=output'
        
        $.ajax(
            {
                url: `${pathname}?${queryStr}`,
                method: 'GET',
                dataType: 'JSON',
                cache: false,
                success: function(productsFetched){
                    console.log(productsFetched);
                }
            }
            );
        }
    }

$(document).ready( function() {

    AA_fetchFilteredProducts();
    
    });

My AA_fetchFilteredProducts function runs on page load, and I modified the Dawn theme’s facets.js document so that my function is also called when any filters on the product grid are changed. It all works except that the results of the AJAX fetch is not even a batch of products, it’s an object with data about a single collection, and not even all the available data.

Why would Shopify be returning a completely different result when the same URL is used in a browser window, versus in an AJAX call?

Could be some jQuery interference? Share a preview link?

My attempt:

templates/collection.data.liquid

{% layout none %}
{{  collection.products | json }}

js code run from dev console:

fetch( 
  location.href + (location.href.includes('?') ? '&' : '?') + 'view=data'
).then(r=> r.json()).then(r=> console.log(r))

Output:

Looks like it was indeed a problem with jQuery. Just using fetch() worked. Thank you Tim!

function AA_fetchFilteredProducts()
    {
    const pathname = window.location.pathname;
    let queryStr = window.location.href.split('?')[1];
    
    if ( queryStr )
        {
        queryStr += ( queryStr.includes('view=output') ) ? '' : '&view=output'
        
        fetch(`${pathname}?${queryStr}`).then(r=> r.json()).then(r=> console.log(r));
        }
    }

1 Like