ssg  
Where to Discuss?

Local Group

Preface

Goal: Build an archive page, sorted by date, and grouped by month and year.

Source Code

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


1: Views: Simple

I like my pages to be tidy. Free from any loop and any formatting tags whenever possible. So I move the whole codes into layout. This way, I can have reusable page parts, so that any pages can utilize that partial layout.

Partial: Nunjucks: Each Post

We should take out this reccuring code to its own nunjucks template.

  <div class="archive-item meta-item">
    <div class="meta_link has-text-right">
      <time class="meta_time is-pulled-right"
            datetime="{{ post.date | date() }}">
        {{ post.date | date('MMM DD, Y') }}&nbsp;
        &nbsp;<span class="fa fa-calendar"></span></time></div>
    <div class="is-pulled-left">
    <a href="{{ post.url | url }}">
      {{ post.data.title }}
    </a></div>
    <div class="is-clearfix"></div>
  </div>

Where the post is defined in whatever layout calling this page.

Partial: Nunjucks: Simple

Now you can move the page content to layout as below.

  <div class="archive-list">
  {%- for post in posts -%}
    {% include "index/each-post.njk" %}
  {%- endfor -%}
  </div>

Where the posts is defined in content page.

Page Content: Nunjucks: Simple Archive

And our old content page would be as simple as this code below:

---
layout    : archive
title     : Simple Archive Loop
eleventyExcludeFromCollections: true
---

{% set posts = collections.posts %}
{% include "index/by-simple.njk" %}

11ty: Layout: Simple Archive

This html page content is, still processed using nunjucks template engine. Have a care, on how posts variable propagate, from collections.posts to post in posts to each post.data.

Render: Browser

Now you can see the result in your favorite browser.

11ty: Page Content: pages/archive-simple


2: Configuration: Sorting Filter

Nunjucks sort filter, require each object to have sorting key.

Sorting with Nunjucks

In order to add sorting capability in post collections with nunjucks, we need to add sort key in each collection object.

{%- set posts = posts | sort(false, true, 'date') -%}

Reference

In official documentation:

Sorting Keys

Unfortunately, with sorting group as above, we cannot group each post, by year or by month easily. This is why we need to build sort keys manually.

Each post object should have year key, such as 1993 or 2020, so that nunjucks can sort it.

.eleventy.js

This sort keys can be obtained by adding filter in configuration.

  eleventyConfig.addNunjucksFilter("mapdate", function(posts) {
    return posts.map(post => ({ 
      ...post,
      year:      moment(post.date).format("Y")
    }));
  });

Partial: Nunjucks: Simple

Nunjucks filter is awesome.

We can use that filter by changing that code above as below:

  {%- set posts = posts | mapdate -%}

  <div class="archive-list">
  {%- for post in posts | sort(true, true, 'year') -%}
    {% include "index/each-post.njk" %}
  {%- endfor -%}
  </div>

Render: Browser

Now you can see the result in your favorite browser.

11ty: Page Content: sorted  pages/archive-simple

.eleventy.js:

For later convenience, I add a few more item in each post object.

  // Custom: Grouping by date
  eleventyConfig.addNunjucksFilter("mapdate", function(posts) {
    return posts.map(post => ({ 
      ...post,
      year:      moment(post.date).format("Y"),
      month:     moment(post.date).format("MM"),
      monthtext: moment(post.date).format("MMMM")
    }));
  });

3: Configuration: Grouping Filter

Beside sorting, we also need grouping.

Grouping Object in Javascript

I simply borrow example reduce code from MDN developers.

I put the code in helper file.

 
exports.groupBy = function(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

This helper code will grow, as we need more function later.

.eleventy.js:

And put the function as filter in configuration.

  // Copy paste from my other tutorial (Hexo)
  eleventyConfig.addNunjucksFilter("groupBy", function(posts, key) {
    return helper.groupBy(posts, key);
  });

Grouping by Date

We can use this groupBy filter, combined with mapdate, to produce multilevel grouping by chronology.

{%- set posts = posts | mapdate -%}
{%- set groupByYear = posts | groupBy('year') | dictsort | reverse -%}

{%- for year, postsInYear in groupByYear -%}
    {%- set groupByMonth = postsInYear | groupBy('month') -%}
    ...
{%- endfor -%}

First grouping by year, and then grouping by month.


4: Views: By Year

That views above is to plain for me. Consider sort the content by date chronology, and also group them by year.

Partial: Nunjucks: By Year

Consider change the layout into a more cutomized situation as below.

{%- set posts = posts | mapdate -%}
{%- set groupByYear = posts | groupBy('year') | dictsort | reverse -%}

<div id="archive">
{%- for year, postsInYear in groupByYear -%}

  <section class="white lighten-5 z-depth-1 hoverable
                  p-5 m-y-5">

    <div class ="anchor-target archive-year" 
         id="{{ year }}">{{ year }}</div>

    <div class="archive-list">
    {%- for post in postsInYear | sort(false, true, 'month') -%}
      {% include "index/each-post.njk" %}
    {%- endfor -%}
    </div>

  </section>

{%- endfor -%}
</div>

Page Content: Nunjucks: Archive by Year

And a short html page content with nunjucks code.

---
layout    : archive
title     : Archive by Year
eleventyExcludeFromCollections: true
---

{% set posts = collections.posts %}
{% include "index/by-year.njk" %}

Render: Browser

Now you can see the result in your favorite browser.

11ty: Page Content: pages/archive-by-year


5: Views: By Month

I nerdily love tree view.

We are not finished yet. We can still regroup each year’s posts, with montly grouping.

Partial: Nunjucks: By Month

Consider change the layout into a complex multilevel grouping as below.

{%- set posts = posts | mapdate -%}
{%- set groupByYear = posts | groupBy('year') | dictsort | reverse -%}

<div id="archive">
{%- for year, postsInYear in groupByYear -%}

  <section class="white lighten-5 z-depth-1 hoverable
                  p-5 m-y-5">

    <div class ="anchor-target archive-year p-b-5" 
         id="{{ year }}">
      {%- if year == (metadata.now | date("Y")) -%}
        <strong>This year's posts ({{ year }})</strong>
      {%- else -%}
        <strong>{{ year }}</strong>
      {%- endif -%}
    </div>

    {%- set groupByMonth = postsInYear | groupBy('month') -%}
    {%- set groupByMonth = groupByMonth | dictsort | reverse -%}

    {%- for month, postsInMonth in groupByMonth -%}
      <div class="p-b-5">

        <div class ="archive-month" 
             id="{{ year }}-{{ month }}">
          {{ postsInMonth[0].monthtext }}</div>

        <div class="archive-list">
          {%- for post in postsInMonth | sort(false, true, 'month') -%}
            {% include "index/each-post.njk" %}
          {%- endfor -%}
        </div>

      </div>
    {%- endfor -%}

  </section>

{%- endfor -%}
</div>

The Year Header

To make the year header nicer, we can rewrite above code to

    <div class ="anchor-target archive-year p-b-5" 
         id="{{ year }}">
      {%- if year == (metadata.now | date("Y")) -%}
        <strong>This year's posts ({{ year }})</strong>
      {%- else -%}
        <strong>{{ year }}</strong>
      {%- endif -%}
    </div>

Page Content: Nunjucks: Archive by Month

And still a short html page content with nunjucks code.

---
layout    : archive
title     : Archive by Month
eleventyExcludeFromCollections: true
---

{% set posts = collections.posts %}
{% include "index/by-month.njk" %}

Render: Browser

Now you can see the result in your favorite browser.

11ty: Page Content: pages/archive-by-month


What is Next ?

Consider continue reading [ Eleventy - Custom Index - Tags ]. We are going to reuse both sorting and grouping filter in tag name. And also build tags page in tree fashioned looks.

Thank you for reading.