Implementing categories
Sun Jan 20 2019I want there to be Categories in Eleventy to organize articles into broad domains.1 The idea is that all articles about books will go into the Culture category, articles about tech go into the Tech category, and so on.
- An article does not have to specify a category.
- An article can belong to only one category.
- Category names are Capitalized by convention.
Tags, on the other hand, can be in any
category. A weird book and
some weird code would be
in different categories,
but they could both be tagged weird
.
How we’ll use them
Let’s look at how we’ll use categories:
- How to specify the category for an article
- How to get an article’s category in a template
- How to work with all of the articles in the same category
Specifying the category
Use the category
property
like this to specify
the category an article belongs to:
---
date: 10/30/2018
title: Loomings
category: Tech
tags:
- tools
- git
- eleventy
---
Using the category in a template
In a template, refer to the category property in the usual way:
<a href="/categories/Tech">Tech</a>
Working with articles in the same category
We can work with articles in the same category
by creating a categories
collection.2
To list
all of the articles in
the Tech
category,
you could do it this way:
<ul>
{%- for article in collections.categories["Tech"] -%}
<li>{{ article.data.title }}</li>
{%- endfor -%}
</ul>
Just as
collections
is an object that has
a property for each tag,
so collections.categories
has a property for each category.
Each property refers to an array of articles.
It looks something like this:
collections: {
all: [ items ],
categories: {
Culture: [ items ],
Life: [ items ],
Thinking: [ items ]
}
}
The implementation
We want:
- a list of categories
- an object that contains a property for each category, and each property is a list of articles for that category
Creating a list of categories
To get the list of categories,
we iterate over all of the
rendered templates.
This code
creates a collection called
categoryList
that contains
the names of all the categories.
getCatList = function(collection) {
let catSet = new Set()
collection.getAllSorted().forEach(item =>
typeof item.data.category === "string"
&& catSet.add(item.data.category))
return [...catSet]
}
eleventyConfig.addCollection("categoryList", getCatList)
Creating a list of articles for each category
To get lists of each article in a category, we want to create an object that has a property for each category, and each property contains a list of articles of that category. In other words, we want to end up with an object that looks like this:
categories {
Culture: [article_1, article_4],
Tech: [article_3],
Life: [article_1, article_3]
}
We can use the
makeCategories()
function
as a callback to
to addCollection()
to create this object.
We iterate over each item
that has a category
property in its
front matter
and add it to the list
for that category:3
makeCategories = function(collection) {
let categories = {}
// Every rendered page
collection.getAllSorted().forEach(item => {
let category = item.data.category
// Ignore the ones without a category
if (typeof category !== "string")
return
if (Array.isArray(categories[category]))
// category array exists? Just push
categories[category].push(item)
else
// Otherwise create it and
// make `item` the first, uh, item.
categories[category] = [item]
})
return categories
}
Since we want to call our collection of categories
categories
, we’d build it like this:
addCollection("categories", makeCategories)
So now we have a way to make a collection that has collections of its own. I use it to segregate my posts into unique buckets.
That’s what I say now. The original impetus for this was to figure out how collections work. ↩︎
You can use any name you like. I happen to like “categories”. ↩︎
This
if (Array.isArray(categories[category]))
thing is really stupid. Isn’t there a way to push to an array, creating it if it doesn’t exist. ↩︎