17
votes

I would like to use Jekyll to create a manual that contains several chapters, each of which contains several sections, and store each section in a separate Markdown file. I want index.md to look something like this:

<ol>
{% for chapter in site.chapters %}
  <li>
    <a href="{{chapter.url}}">{{chapter.title}}</a>
    <ol>
    {% for section in chapter.sections %}
      <li><a href="{{section.url}}">{{section.title}}</a></li>
    {% endfor %}
    </ol>
  </li>
{% endfor %}
<ol>

If each chapter is a Markdown file in _chapters, and I add the right lines to _config.yml, I can iterate over the chapters, pull out the title field from the YAML header, etc. Is there a way to nest this? I've tried creating sub-directories under _chapters with various names, but (a) Jekyll doesn't pick them up as sub-collections and (b) there's no obvious place to store chapter-level information (like the chapter's overall title).

(Note: I think I can do this by explicitly enumerating chapters and sections in a block of YAML in _config.yml, or by creating a separate YAML file in _data, but I don't want to have to worry about keeping that enumeration in sync with the actual chapters and sections: I'd like Jekyll to pick up changes automatically.)

2

2 Answers

22
votes

So the best way I know how to do this is to invert your thinking.

You're not writing chapters, you're writing sections and organising them into chapters.

Assume you have a setup like this:

  • _sections
    • section01.md
    • section02.md
    • section03.md
    • section04.md

But you want it output like this:

  • _site
    • chapters
      • chapter1.html (contains section01.md followed by section03.md)
      • chapter2.html (contains section02.md followed by section04.md)

If that's the case, you can do so using collections. Set the chapter property in the frontmatter of section01.md and section03.md to 01 and the chapter property in the frontmatter of section02.md and section04.md to 02.

The problem is generating the pages for the chapters. You do need to make a page for each chapter, but it's not bad if you use a layout.

Here's the layout I used (in _layouts/chapter.html):

---
layout: default
---

<h1>Chapter {{ page.chapter }}</h1>

{% for section in site.sections %}
{% if section.chapter == page.chapter %}
{{ section.output }}
{% endif %}
{% endfor %}

Then in _chapters, I have chapter01.md, which looks like this:

---
chapter: 01
layout: chapter
---

Just copy that to chapter02.md and set the chapter property to 02, and now you have Chapter 2.

The only other thing required to make this work is updating your config:

collections:
        sections:
                output: false
        chapters:
                output: true

When you run jekyll build, you'll now have _site/chapters/chapter01.html and _site/chapters/chapter02.html. As new sections get created, they'll be added to whatever chapter is in their frontmatter.

This is all really confusing, so I set up a sample at http://paddycarver.github.io/jekyll-nested-collections-example/ with source code at https://github.com/paddycarver/jekyll-nested-collections-example.

0
votes

One thing to be noted for _collection is the same as _post:

The nested files are treated the same as the files in the root folder.

You could arrange the markdown files using weight as below:

_chapter
   chapter1.md <- put weight: 10
   chapter2.md <- put weight: 20
   _chapter/section1
      section11.md <- put weight: 11
      section12.md <- put weight: 12
   _chapter/section2
      section21.md <- put weight: 21
      section22.md <- put weight: 22

then sort to the index file by their path like this

<ol>
{% assign items = site.chapter | sort: 'weight' %}
{% for item in items %}
  {% if not item.path contains '/section' %}
  <li>
    <a href="{{item.url}}">{{item.title}}</a>
    <ol>
  {% else %}
      <li><a href="{{item.url}}">{{item.title}}</a></li>
  {% endif %}
    </ol>
  </li>
{% endfor %}
<ol>

output

<ol>
  <li>
    <a href="chapter1.html">chapter1</a>
    <ol>
      <li><a href="section11.html">section11</a></li>
      <li><a href="section12.html">section12</a></li>
    </ol>
  </li>
  <li>
    <a href="chapter2.html">chapter2</a>
    <ol>
      <li><a href="section21.html">section21</a></li>
      <li><a href="section22.html">section22</a></li>
    </ol>
  </li>
<ol>