I have posts in my 11ty blog - books, whose names started on some letters. For example, i have 12 books: 4 with first letter "A", 4 with first letter "F", 4 with first letter "Q".
And I have a code for printing all books (I use njk in blog layouts):
<div>
{% for book in collections.books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endif %}
{% endfor %}
</div>
I need to print in front of all books with the letter "A":
<h2>A</h2>
Аfter that there will be 4 books with the letter "A". And after this books, will be the same heading, but with letter "F":
<h2>F</h2>
How to do it? In njk, it is difficult to assign/rewrite and manipulate variables. Maybe there is some normal example?
You can use Nunjucks's set
tag to create and modify a variable that keeps track of the previous initial letter:
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
Demo:
const data = {
collections: {
books: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
}
const template = `
<div>
{% set previousInitial = null %}
{% for book in collections.books %}
{% if book.data.name[0] != previousInitial %}
<h2>{{ book.data.name[0] }}</h2>
{% endif %}
{% set previousInitial = book.data.name[0] %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks@3.2.3/browser/nunjucks.min.js"></script>
If the items in collections.books
are not already sorted by their names, you can sort them with Nunjucks's sort
filter:
{% for book in collections.books|sort(attribute='data.name') %}
An alternative is to create a new collection. Consider creating a collection of books already categorized by their initial letters, so you can reduce the amount of logic in your Nunjucks template. Example of how the new collection and template could look like:
const data = {
collections: {
booksCategorizedByInitialLetters: {
A: [
{ data: { name: 'ABC Book', text: 'Learn the alphabets!' }, url: '/book/abc-book/' },
{ data: { name: 'Another A book', text: 'A nice book' }, url: '/book/another-a-book/' },
],
F: [
{ data: { name: 'Foo...', text: '...bar and baz' }, url: '/book/foo/' },
],
},
},
}
const template = `
<div>
{% for initialLetter, books in collections.booksCategorizedByInitialLetters %}
<h2>{{ initialLetter }}</h2>
{% for book in books %}
<a class="book" href="{{ book.url }}">
<article>
<h3 class="text-link">
{{ book.data.name }}
</h3>
<p>{{ book.data.text }}</p>
</article>
</a>
{% endfor %}
{% endfor %}
</div>
`
console.log(nunjucks.renderString(template, data))
.as-console-wrapper { max-height: 100% !important; }
<script src="https://unpkg.com/nunjucks@3.2.3/browser/nunjucks.min.js"></script>
(I'm not sure whether it's possible to create a collection whose type is an object, but IIRC it should be possible.)