javascriptajaxshopifyliquidjquery-ajax

Ajax - Filter by price in Shopify / Liquid


I have a collection which I want to be able to filter (not sort) products by price. I do not want to use an app.

I am aware of this question Add price filter in shopify? however this is not what I am looking for; I want to be able to choose any combination of numbers e.g. between $4 and $20, or $7 and $30, etc, that the user can change, not just a predetermined range. I have seen it done on a Shopify site and therefore know it can be done. I believe this will require ajax calls.

An answer would be extremely helpful both to myself and to many others as this seems to be a very basic requirement which remains unanswered on SO / Shopify forums, even if just a general instruction rather than any specific code. For reference I am using the default (debut) theme.


Solution

  • My project is similar to yours so fill in the blank below.

    create a collection template collection.price-filter.liquid
    use ajax to get the products json. of course there is a limit of 250. If you have more than 250 then needed to use an app to filter the result first. or, just use multiple request.

    <script>
        var allProducts;
        var filteredProducts = [];
        var activeFilter = [];
        $(document).ready(function(){
            var url = '{{collection.url}}/products.json?limit=250';
            const res = getProducts(url);
        });
    
        function getProducts(url){
            $.ajax({
                type: 'GET',
                url: url,
                success: function(res){ 
    
                    allProducts = res.products;
                    filterProducts(allProducts);
                },
                error: function(status){
                     alert(status);
                }
            });
        }
    </script>
    

    filter the product to fill in filteredProducts array objects

    //loop through the products, create categories ($20-$40, $40-$60, etc.)
    function filterProducts(products){
        var cat1 = '20-40', cat2 = '40-60';
        var currentCat;
        products.forEach(function (i, j){   
            if (i.price > 20 && i.price <= 40){
                currentCat = cat1;
            else if (i.price > 40 && i.price <= 60) {
                currentCat = cat2;
            }
                if(filteredProducts[currentCat]){
                    filteredProducts[currentCat].push(i);
                }
                else {
                    filteredProducts[currentCat] = [i]
                }
            };
        });
    }
    

    After generated filteredProducts, listen to checkbox selection from User, add the selection into an array called activeFilter

    function getActiveFilter(){
        $('#myFilter').each(function(){
            if($(this).is(':checked')){
                //add to activeFilter
            }
        });
    }
    

    Generate resultFilter from filteredProducts and activeFilter, eliminate common result;

    function resultFilter(){
        var result = [];
        activeFilter.forEach(function (i, value){
            Object.keys(value).forEach(function(j, product){
                //add to result
            };
        });
    }
    

    Build/show product result using handlebars.js

    <script id="ProductTemplate" type="text/x-handlebars-template">
      {% raw %}
        {{#product}}
            <div class="col">
              <a href="/products/{{productHandle}}" class="grid__image">
                <div class="product__image-wrapper" style="background-color: white;">
                  <img class="no-js lazyload" width="316" height="237"
                    data-src="{{ featuredImg }}"
                    data-widths="[108, 360, 375, 414, 568, 684, 720, 732, 736, 768, 1024, 1200, 1296, 1512, 1728, 1944, 2048]"
                    data-aspectratio="1.33"
                    data-sizes="auto"
                    data-parent-fit="width"
                    alt="{{ title }}">
                </div>
              </a>
              <p class="h5"><a href="/products/{{productHandle}}">{{ title }}</a></p>
            </div>
        {{/product}}
       {% endraw %}
    </script>
    

    Insert data into #container-products, where you want the filtered Products to show.

    function buildFilteredProducts(filteredProds) {
            var $productContainer = $('#container-product');
            $productContainer.empty();
            if(filteredProds.length <= 0){
                $productContainer.append('empty result');
                return;
            }
    
            var products = [];
            var product = {};
            var data = {};
            var source = $("#ProductTemplate").html();
            var template = Handlebars.compile(source);
            products = filteredProds.map(function(productItem) {
                return product = {
                id: productItem.id, 
                title: decodeHtml(productItem.title),
                featuredImg: responsiveImage(productItem.images[0].src),
                productHandle: productItem.handle
                }
            });
    
            data = {
                product: products
            } 
    
            $productContainer.append(template(data)); 
        }