Filter Products by Categories

I have categories defined in the front matter of all my products in the _products folder. Is there a way I can setup buttons or links to click to filter my products page (index.html) by the categories define?

Thanks,

If you use posts and categories then you can iterate over site.categories

So you’ll have to make a category listing page by iterating over all products and getting unique categories and outputting that
And then use a generator plugin to make a page for each category

And way with less code is assuming only one category for a product is to use folder structure instead of metadata.

If you have

_products
  _catA
     prod1.md
     prod2.md
     index.md # this can have Category A as the title
  _catB
    prod3.md
    index.md

list.md

And you add products as a collection under config file

Then you can iterate over something like

site.collections.products

To get the category names.

And the index is the category page which can list all items in the category.

The list.md is to list all available categories. There is no duplication to worry about and I think they’ll already get alphabetically sorted

I follow that pattern here for music collection as all the albums of my band
Then each album has an index page which lists songs on the album
And the songs are files on each album folder

So items are grouped by filename and not metadata

Thanks for replying. I was hoping to possibly preserve the structure of my project though. What do you think about this http://www.cagrimmett.com/projects/2016/09/15/jekyll-categories-isotope.html
Could that work?

Thanks,

Note that article assumes you want to use posts that have categories. It uses site.categories

What you want to do requires building up a list of category names on your products and then iterate over that. And then for a given category output all products for it.

You’re going to need a generator plugin to make those. Or you can make the category pages by hand just you’ll just have to remember to do that.

_products
  1.md
  2.md
_product_categories
  a.md
  b.md

Here there is no product data in the a.md file. Just a title for the category. And a layout can be used to get all products for the current category. You can get page.path to get a.md from the page
Then use a where filter to get products where page.path is in categories array for a product.

Alternatively you can list products inside a category

e.g.

a.md

---
title: A
products:
  - 1
  - 2
---

i’m doing something similar, but haven’t found a way to do this without creating a plugin similar to jekyll-archives. that plugin provides templated tag and category pages that provides my desired functionality, but only for posts, not for other site collections.

i’ve gotten as far as being able to list collections into tags and categories, but not have specific pages (e.g., filtered pages) templatized for each. that’s where the plugin would be step in.

i used the following technique to collect tags (works for any array values set in front matter) so that i can iterate over each tag and list out the related items (products in this case) on an index/list page:

{%- assign product_tags = site.products | map: 'tags' | join: '-----' | split: '-----' | uniq -%}

{% for tag in product_tags -%}
  {%- assign products = site.products | where_exp: 'product', "product.tags contains tag" -%}
  <h1>{{ tag }}</h1>
  <ul>
    {%- for product in products %}
      <li>
        <a href="{{-product.url-}}">{{-product.title-}}</a>
      </li>
    {%- endfor %}
  </ol>
{%- endfor %}

this would list every product tag and then list every product with that tag under it, all on one page. the next step, the one that requires a plugin to generate each templatized page (otherwise you have to manually create each tag page), is to be able to link each tag heading to a templatized tag page that can list only the products for that tag.

the other option is to use a javascript library that operates on hidden attributes on each product to show and hide them on the index page using buttons, a dropdown, or another method. i don’t need this functionality, so i haven’t explored solutions for this option.

edit: yup, that isotope method could work as a javascript solution.

Thanks for sharing.

What happens if you take out join and split?

What does this do? I’ve only seen {%- if %} before.

{{-product.url-}}

See also this from related question

{% assign all_tags = tags | split: " " | uniq | sort %}

i found that trick somewhere on stackoverflow (forgot where)–it’s a way of decomposing a list of arrays into components and then recomposing them into one array within the constraints of liquid not allowing you to create or merge arrays natively. otherwise you get a concatenated string of the stringified arrays (with brackets and such). oh, also, it avoids messing up tags with spaces in ithem.

oh, i think that’s a holdover from something else, where i was trying to see how well the whitespace control in liquid allows you to structure the output html. basically, the dash tells liquid to ignore whitespace on that side of the tag/variable, so having them on both sides eats all the whitespace around it, making it snug to the wrapping quotes or html tags for example.

Thank you all for your help. Someone help me to get it to work by doing the following:

This section will list all the tags/“categories” into buttons:

{% assign category_types = site.products | map: 'category' | join: ',' | join: ',' | split: ',' | uniq | sort %} Show All {% for category in category_types %} {% if category != "" %} {{ category }} {% endif %} {% endfor %}

This part below is the main grid to filter the “grid” div:

{% for product in site.products %}
      <div class="product-details">
        <div class="product-name">
          <h4><a href="{{ product.url }}">{{ product.name }}</a></h4>
        </div>
        <div class="product-price">
          <p>&#x24;{{ product.price }} {{ site.currency }}</p>
        </div>
      </div>
    </section>
    {% endfor %}
  </main>

This part below is the javascript for the buttons:

  <script>
    $(function () {
      $('.filtering button').click(function () {
        var get_id = this.id;
        var get_current = $('.grid .' + get_id);
        $('.entry').not(get_current).hide(500);
        get_current.show(500);
      });
      $('#showall').click(function () {
        $('.entry').show(500);
      });
    });
  </script>

NOTE: you have to have javascript on the website already!