Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hierarchical Categories in GitHub Pages

I'm using Jekyll on GitHub pages, and I want to have hierarchical categories like this:

  • animals -> mammals -> cats -> _posts -> housecat.md, tiger.md
  • animals -> mammals -> dogs -> _posts -> poodle.md, doberman.md
  • animals -> reptiles -> lizards -> _posts -> iguana.md, chameleon.md

I'd like users to be able to visit /animals and see a listing of every post from every category. But if they go to /animals/mammals, they'd only see mammals. If they go to /animals/mammals/cats, then they only see cats.

I know I can do this manually by putting an index.html file in every single directory and then looping through site.categories.mammals or site.categories.cats, for example.

But that seems a little bit too brute force, and I'm hoping there's a better way. If I want to change how I'm showing the listings, I'll have to change that in every single subcategory. I'll also have problems when subcategories share a name, like /ABC/XYZ/_posts/one.md and /DEF/XYZ/_posts/two.md.

I've tried to follow this article, which uses one main category.html page that loops through page.category:

{% for post in site.categories.[page.category] %}
  <h2><a href=""></a></h2>
  <p></p>
{% endfor %}

Then every index.html file uses this as its layout. That almost works, but it seems limited to one category, not multiple hierarchical categories.

Is there a less brute-force approach to creating listings for hierarchical categories?

like image 285
Kevin Workman Avatar asked Aug 01 '16 13:08

Kevin Workman


2 Answers

page.categories is a list

https://stackoverflow.com/a/23927986

{% for cat in page.categories %}
  <h1>{{ cat }}</h1>
  <ul>
    {% for post in site.categories[cat] %}
      <li><a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endfor %}
  </ul>
{% endfor %}

From jekyll's documentation about page.category http://jekyllrb.com/docs/variables/#page-variables

The list of categories to which this post belongs. Categories are derived from the directory structure above the _posts directory. For example, a post at /work/code/_posts/2008-12-24-closures.md would have this field set to ['work', 'code']. These can also be specified in the YAML Front Matter.

You should be easily able to add a simple dynamic index.html to every folder and the categories should be hierarchical automatically.

Update

The above does NOT work. You need to treat the combination of categories of each hierarchy as a unique item. Concat the index page's categories, and compare that against all the posts in the site.

/foo/bar/_posts/2016-08-01-foo-bar-test.html

---
categories:
  - foo
  - bar
title: test foo bar
---

<h2>Foo Bar</h2>

/var/bar/_posts/2016-08-01-test-var-bar.html

---
categories:
  - var
  - bar
title: test var bar
---

<h2>Var Bar</h2>

/foo/bar/index.html

---
categories:
 - foo
 - bar
---

{% assign pagecat = page.categories | join ' ' | append: ' '%}
{% assign pagecatlen = page.categories.size %}
  <h1>{{ cat }}</h1>
  <ul>
    {% for post in site.posts %}
    {% assign postcat = '' %}
    {% for thispostcat in post.categories limit: pagecatlen %}
      {% assign postcat = postcat | append: thispostcat %}
      {% assign postcat = postcat | append: ' ' %}
    {% endfor %}

    {% if (postcat == pagecat) %}
      <li><a href="{{ post.url }}">{{ post.title }}</a></li>
    {% endif %}
    {% endfor %}
  </ul>

The categories are optional for the files in _posts, but they are required for the front matter of each index file.

like image 179
chugadie Avatar answered Nov 19 '22 15:11

chugadie


Modify your _config.yml accordingly

collections:
    animals:
        output: true
        mammals:
            output: true
            cats:
                output: true
            dogs:
                output: true
        reptiles:
            output: true
            lizards:
                output: true

then created the structure:

mkdir -p _animals/reptiles/lizards
mkdir -p _animals/mammals/cats
mkdir _animals/mammals/dogs

add your md files and all index.html which will index items with filter. It should look like this (maybe with more indexes) :

_animals/
├── index.html
├── mammals
│   ├── cats
│   │   ├── housecat.md
│   │   └── tiger.md
│   ├── dogs
│   │   ├── doberman.md
│   │   └── poodle.md
│   └── index.html
└── reptiles
    └── lizards
        ├── chameleon.md
        └── iguana.md

then you create _includes/list_animals.html

{% assign animals = site.animals| sort:'title' %}
{% for animal in animals %}
{% if page.url != animal.url  and include.taxonomy == nil or animal.url contains include.taxonomy %}
<a  href={{ animal.url | prepend: site.baseurl }}>{{animal.title}}</a>
{% endif %}
{% endfor %} 

to list all animals in animals/index.html :

---
title: animals
---
{% include list_animals.html %}

For example, to list all mammals in animals/mammals/index.html :

---
title: animals
---
{% include list_animals.html taxonomy="mammals" %}

Finally the generated structure should look like this (with some more index.html):

_site
└── animals
    ├── index.html
    ├── mammals
    │   ├── cats
    │   │   ├── housecat.html
    │   │   └── tiger.html
    │   ├── dogs
    │   │   ├── doberman.html
    │   │   └── poodle.html
    │   └── index.html
    └── reptiles
        └── lizards
            ├── chameleon.html
            └── iguana.html
like image 27
jibe Avatar answered Nov 19 '22 15:11

jibe