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') }}
<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" %}
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.
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.
.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.
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.
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.