ssg  

Preface

Goal: Apply Tag Collection based on Zach’s Code.

Source Code

This article use tutor-03 theme. We will create it step by step.


1: About Collections

Freedom

Just like most SSG, eleventy can show article index, by chronology, or by tags. Those content can be queried, filtered, and finally shown, as the site author decide to. Eleventy arrange this content list with collections.

Eleventy comes with built in tag collection. This way, eleventy can create tag based list manually. Although first this looks thougher than most common SSG, this eleventy approach also free you from SSG limitation. Because you can freely make your own kind of collections

Official Documentation

Reference


2: Archive

Before we go on into custom collections, consider refresh our memory to our simple default collections.

Reference

Page Content: pages/archive

---
layout    : archive
title     : Archive
permalink : /pages/
eleventyExcludeFromCollections: true
---

  <ul>
    {%- for post in collections.all | reverse -%}
    <li>
      <a href="{{ post.url | url }}">
        {{ post.data.title }}</a>
    </li>
    {%- endfor -%}
  </ul>

This time this content wears archive layout.

Layout: Nunjucks Archive

Template inheritance in nunjucks, start with the word extends.

{% extends "layouts/base.njk" %}

{% block main %}
  <h2>{{ title or metadata.title }}</h2>
  <strong>This is an archive kind layout.</strong>
  <br/>

  {{ content | safe }}
{% endblock %}

Render: Browser

Now you can see the result in the browser.

11ty: Page Content: pages/archive

The Loop

The loop is as simple as all collections, but shown backward:

    {% for post in collections.all | reverse %}
      ...
    {% endfor %}

The word reverse here is builtin filter from nunjucks.


3: Keys in Collections

The Riddle?

What other keys are inside collections?

So what is this collections.all after all? Or let me rephrase with better question. What’s inside these collections?

.eleventy.js

To answer this we need other other filter in .eleventy.js.

  // values Filter: MDN web docs
  eleventyConfig.addNunjucksFilter("keys", function(array) {
    return Object.keys(array);
  });

Page Content: pages/keys

And apply to views

  <p>collections: [{{ collections | keys | join(", ") }}]</p>

Render: Browser

Now you can see the result in the browser.

collections: [all, subtitle, story, tagList]

It turned out that collections has four members

  • all (default)

  • subtitle (tag)

  • story (tag)

  • tagList (manual collections)

11ty: Page Content: pages/keys

Manual

We have this tagList from manual collection in .eleventy.js.

  // Copy paste from Zach
  eleventyConfig.addCollection("tagList",
    require("./views/_11ty/getTagList"));

Tags

And we have these subtitle and story from frontmatter.

layout    : post
title     : Every Day
date      : 2015-10-03 08:08:15
slug      : every-day
tags      : ['subtitle', 'story']

4: Set (ECMAScript 2015)

Before we get down to generate each tag page, consider this simple javascript.

Reference

The Songs Script

This script utilize Set introduced in ECMAScript 2015, to collect tag names. And convert the the set output back simple array.

songs  = [
  { title: "Cantaloupe Island",          tags: ["60s", "jazz"] },
  { title: "Let it Be",                  tags: ["60s", "rock"] },
  { title: "Knockin' on Heaven's Door",  tags: ["70s", "rock"] },
  { title: "Emotion",                    tags: ["70s", "pop"] },
  { title: "The River" }
];

let tagSet = new Set();

// using set feature to collect tag names
songs.forEach(function(song) {
  if( "tags" in song ) {   
    let tags = song.tags;
    console.log(tags);

    for (const tag of tags) {
      tagSet.add(tag);
    }
  }
});

console.log(tagSet);

// normalize to array
let alltags = [...tagSet];

console.log(alltags);

11ty: Set in ECMA2015

Running in CLI

Run this script, and you will get

$ node songs.js
[ '60s', 'jazz' ]
[ '60s', 'rock' ]
[ '70s', 'rock' ]
[ '70s', 'pop' ]
Set(5) { '60s', 'jazz', 'rock', '70s', 'pop' }
[ '60s', 'jazz', 'rock', '70s', 'pop' ]

This way, you can apply to 11ty tags in frontmatter.

Page Content: every-day.md

---
layout    : post
title     : Every Day
date      : 2015-10-03 08:08:15
slug      : every-day
tags      : ['subtitle', 'story']
---

I've been thinking about this
over and over and over.
<!-- more --> 

I mean, really, truly,
imagine it.

Have a look at this line in frontmatter:

tags      : ['subtitle', 'story']

These tags in frontmatter will be, compiled internally by 11ty into collections. So later, you can access directly as below:

  {% set postslist = collections[ tag ] %}

5: Collections: getTagList.js

Consider apply those script above to eleventy.

.eleventy.js

We are going to add this script manually as a collections.

  // Copy paste from Zach
  eleventyConfig.addCollection("tagList",
    require("./views/_11ty/getTagList"));

Complete Script

You can use complete script from here:

Simpler Script

For explanation purpose, I stripped down the script.

The underscore in _11ty, means this folder is not a website content, and won’t be rendered. Very useful for utility folder.

module.exports = function(collection) {
  let tagSet = new Set();

  collection.getAll().forEach(function(item) {
    if( "tags" in item.data ) {
      let tags = item.data.tags;

      for (const tag of tags) {
        tagSet.add(tag);
      }
    }
  });

  return [...tagSet];
};

11ty: ViM getTagList.js

This script is very similar to previous CLI script above.


6: Loop: All Tags

With the result of above tagList collections, we can just show the tag, just like we display the index page.

Page Content: tags.html

---
layout    : tags
title     : List of Tags
eleventyExcludeFromCollections: true
---

  <ul>
    {% for tag in collections.tagList %}
    <li>
      {% set tagUrl %}/tags/{{ tag }}/{% endset %}
      <a href="{{ tagUrl | url }}" class="tag">{{ tag }}</a>
    </li>
    {%- endfor -%}
  </ul>

Layout: Nunjucks Tags

Nothing special yet about this layout.

Template inheritance in nunjucks, start with the word extends.

{% extends "layouts/base.njk" %}

{% block main %}
  <h2>{{ title or metadata.title }}</h2>
  <strong>This is a tags kind layout.</strong>
  <br/>

  {{ content | safe }}
{% endblock %}

Render: Browser

Now you can see the result in the browser.

11ty: Page Content: tags

The Loop

The loop is as simple as all collections, but shown backward:

    {% for tag in collections.tagList %}
      ...
    {% endfor %}

We use manually crafted taglist collection.


7: Pagination: Tag Name

The last page is a little bit hard, because this page utilize pagination. And pagination in eleventy have weird approach.

This page will generate many pages. One page for each tag name.

Reference

A brilliant approach.

Paginated Content: tag-name.html

Pagination also can be used to manage tag names.

---
layout    : tag-name
eleventyExcludeFromCollections: true

pagination:
  data: collections
  size: 1
  alias: tag
  filter: tagList

permalink: /tags/{{ tag }}/
---

Directory Tree

This arrangement will automatically generate tag pages as below:

11ty: Tree of Tag Names

Notice there is also all tags.

Filter

I add filter: tagList, to avoid tagList/index.html comes out in the tree.


8: Render Data

Beside permalink, we can render any data, with pagination. For example we can change the title to reflect current tag.

---
...
permalink: /tags/{{ tag }}/

renderData:
  title: Tagged {{ tag }}
---

We can use this render data in layout.

Layout: Nunjucks Tag Name

Beware of the difference. This layout use renderData.title.

{% extends "layouts/base.njk" %}

{% block main %}
  <h2>{{ renderData.title or title or metadata.title }}</h2>
  <strong>This is a tag-name kind layout.</strong>
  <br/>

  {{ content | safe }}
{% endblock %}

Layout: Nunjucks Head

Also use it in head.

  <title>{{ renderData.title or title or metadata.title }}</title>
  <link href="/assets/favicon.ico"
        rel="shortcut icon" type="image/x-icon" />

9: Using Collections Object

We still have to output the post for each tag.

Reference

The code is almost verbatim copy, from this link below.

Paginated Content: tag-name.html

---
layout    : tag-name
eleventyExcludeFromCollections: true

pagination:
  data: collections
  size: 1
  alias: tag

permalink: /tags/{{ tag }}/

renderData:
  title: Tagged {{ tag }}

---

  {% set postslist = collections[ tag ] %}
  <ul>
    {%- for post in postslist -%}
    <li>
      <a href="{{ post.url | url }}">
        {{ post.data.title }}</a>
    </li>
    {%- endfor -%}
  </ul>
  
  <p>List of <a href="{{ '/tags/' | url }}">all tags</a>.</p>

11ty: NERDTree of Tag Names with Source

Render: Browser

Now you can see the result in the browser.

11ty: Page Content: tag-name

The Loop

The loop is just about using collections.

    {%- for post in collections[ tag ] -%}
       ...
    {%- endfor -%}

Wait?

Where is this collections[ tag ] comes from???

Explanation

The official 11ty site said:

First up notice how we’re pointing our pagination
to iterate over collections,
which is an object keyed with tag names pointing
to the collection of content containing that tag.

This will be clear in this reference:

Allright, the hardest part is over. The next chapter should be easy.


What is Next ?

Consider continue reading [ Eleventy - Bulma - CSS Intro ]. We need to revamp the looks of this good eleventy, with proper stylesheet.

Thank you for reading.