'How to create a Tags collection and a Categories collection in Eleventy
I have the following two blocks of code in my .eleventy.js
file
// Tags
eleventyConfig.addCollection('tagList', collection => {
const tagsSet = new Set();
collection.getAll().forEach(item => {
if (!item.data.tags) return;
item.data.tags.filter(tag => !['posts', 'all'].includes(tag)).forEach(tag => tagsSet.add(tag));
});
return Array.from(tagsSet).sort();
});
// Categories
eleventyConfig.addCollection('categoryList', collection => {
let catSet = new Set();
collection.getAll().forEach(item => {
if (!item.data.categories) return;
item.data.categories.filter(cat => !['posts', 'all'].includes(cat)).forEach(cat => catSet.add(cat));
});
return Array.from(catSet).sort();
});
In my posts I Have
---
layout: post
title: 'Whatever'
date: '2021-10-12'
permalink: '{{ title | slug }}/index.html'
categories:
- code
tags:
- javascript
- angular
---
And i'm using a template like this to auto generate pages for each Tag (and a similar one for categories).
---
layout: page
eleventyComputed:
title: All posts tagged '{{ tag }}'
permalink: /tags/{{ tag }}/
pagination:
data: collections
size: 1
alias: tag
filter:
- all
- nav
- post
- posts
- tagList
addAllPagesToCollections: true
permalink: /tags/{{ tag }}/
headerButton:
url: '/blog/'
text: All posts
---
<ul class="unstyled">
{%- for post in collections[tag].reverse() -%}
{%- if not post.data.draft -%}
<li>{% include 'post-listing.njk' %}</li>
{%- endif -%}
{%- endfor -%}
</ul>
For tags, everything is working fine. I get the correct pages generated i.e. /tags/javascript
but for categories, its generating pages based on the Tags and not the categories. So instead of /categories/code
I'm ending up with /categories/javascript
I'm assuming the issue is with my eleventyConfig.addCollection('categoryList')
function but I have no idea why.
Solution 1:[1]
Your issue might be in the pagination in your template. You're paginating on the collections
object, which is an object mapping tags (a special 11ty front matter attribute) to pages with that tag.
Since you're creating a list of tags and categories as collection items, you'll want to paginate that collection instead.
---
# ...
pagination:
data: collections.tagList # (or collections.categoryList)
size: 1
alias: tag
# ...
---
This might pose an issue since you're accessing the posts themselves in the template, so you need a way to map the tag/category back to the list of posts. You can fix this by changing your collections to an Object
instead of an Array
.
// Tags
eleventyConfig.addCollection('tagList', collection => {
const tagsSet = {};
collection.getAll().forEach(item => {
if (!item.data.tags) return;
item.data.tags.filter(
tag => !['posts', 'all'].includes(tag)
).forEach(
tag => {
if (!tagsSet[tag]) { tagsSet[tag] = []; }
tagsSet[tag].push(item)
}
);
});
return tagsSet;
});
// Categories
eleventyConfig.addCollection('categoryList', collection => {
let catSet = {};
collection.getAll().forEach(item => {
if (!item.data.categories) return;
item.data.categories.filter(
cat => !['posts', 'all'].includes(cat)
).forEach(
cat => {
if (!catSet[cat]) { catSet[cat] = []; }
catSet[cat].push(item)
}
);
});
return catSet;
});
Now, when you want to access the posts, you can index into collections.tagList
(or collections.categoryList
).
{%- for post in collections.tagList[tag] | reverse -%}
Solution 2:[2]
Everything you suggested above has worked. I now have separate pages for tags and categories generated, so thank you for that.
The issue I now have is, on my Blog page, i previously had a list of Tags being output like so
<div class="tags tags--buttons">
{%- for tag in collections.tagList -%}
<a href="/tags/{{tag}}">{{tag}}</a>
{%- endfor -%}
</div>
This no longer outputs anything since the collection is no longer an array.
I updated to
<div class="tags tags--buttons">
{%- for tag, val in collections.tagList -%}
<a href="/tags/{{tag}}">{{tag}}</a>
{%- endfor -%}
</div>
and this works fine for Tags, but its not working for collections (using collections.categoryList
)
Also, the tags are no longer in alphabetical order
Any suggestions?
UPDATE:
The code for outputting categories is
{%- for cat, val in collections.categoryList | dictsort -%}
{%- if cat !== "posts" -%}
<a href="/categories/{{cat}}">{{cat}}</a>
{%- endif -%}
{%- endfor -%}
This doesn't give any errors, but also no output.
If I add | dictsort
to it, then I get the error Template render error: dictsort filter: val must be an object
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |