How to filter Taxonomies using Rust-based Zola / Tera? - template-engine

I have recently discovered Zola and Tera (Rust frameworks for statically-generated websites) and found them amazing.
I'm trying to filter specific category pages to display in a section on the same page. To illustrate, I wrote some code like this:
<div class="content">
{% block content %}
<div class="list-posts">
{% for page in section.pages %}
{% for key, taxonomy in page.taxonomies %}
{% if key == "categories" %}
{% set categories = taxonomy %}
{% for category in categories %}
{% if category == "rust" %}
<article>
<h3 class="post__title">{{ page.title }}</h3>
</article>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endblock content %}
</div>
There should be MULTIPLE sections of the code above for different categories, e.g. "rust", "java", etc.
I wrote the code to explain my question, but it isn't the way I want it (and it doesn't work when the sections are duplicated).
How do I do the filtering of the particular category when the sections/pages are loaded?
The front-matter metadata in the content file is:
title = "A web page title"
[taxonomies]
categories = ["rust"]
If you see my example code above, I have to access it first via a hash map, then an array, in order to filter all pages which is "rust".
The filter below doesn't work:
for page in section.pages | filter(attribute="taxonomies.categories", value="rust"

I managed to resolve it. First, I did tests like this:
HTML test print output
{% set categories = get_taxonomy(kind="categories") %}
{% set rustItems = categories.items | filter(attribute="name", value="rust") %}
{% set javaItems = categories.items | filter(attribute="name", value="java") %}
{{ rustItems[0].pages | length }}
<br>
{{ rustItems[0].pages[0].title }}
<br>
{{ rustItems[0].pages[1].title }}
<br>
I was able to pick up the title as set in the .md file.
So I moved on further and I did:
{% set categories = get_taxonomy(kind="categories") %}
{% set category = categories.items | filter(attribute="name", value="business") | first %}
{% for page in category.pages %}
{{ page.title }}
... etc.
The above code will filter the pages for category taxonomy.

Related

How can I specify an 11ty collection in frontmatter?

On my website, I want to show a list of collection items on pages corresponding to each collection. For example on the Games page, I want to show a list of all game related articles, on the Politics page, I want to show a list of all politics related articles, etc.
What I have right now is one Nunjucks template file for each such page, which I don't particularly like since they are the same except for the collection to show. Here is two of my templates, for my politics articles and for my games articles, where I extend a common boilerplate (base.njk) and in the main block, I first print some Markdown content and then comes the list of collection items:
{# politics.njk #}
{% extends "base.njk" %}
{% block main %}
<main>
{{ content | safe }}
<ul class="postlist no-bullets">
{% for item in collections.politics %}
{% include "partials/collection-list-item.njk" %}
{% endfor %}
</ul>
</main>
{% endblock %}
{# games.njk #}
{% extends "base.njk" %}
{% block main %}
<main>
{{ content | safe }}
<ul class="postlist no-bullets">
{% for item in collections.games %}
{% include "partials/collection-list-item.njk" %}
{% endfor %}
</ul>
</main>
{% endblock %}
As you can see, the only difference between these two files is collections.politics and collections.games. I would want to use one template, and in the frontmatter specify which collection I want. Since I'm already having one Markdown file per page the template used (for writing stuff before I begin listing articles), it would have been nice if it was possible. For example:
<!-- politics.md -->
---
layout: articles-list.njk
title: Politics
listCollection: collections.politics
---
# Politics
Below is a list of all politics articles I've written.
<!-- games.md -->
---
layout: articles-list.njk
title: Games
listCollection: collections.games
---
# Games
I think about games a lot. Below is a list of articles I've written on the topic.
{# articles-list.njk #}
{% extends "base.njk" %}
{% block main %}
<main>
{{ content | safe }}
<ul class="postlist no-bullets">
{% for item in listCollection %}
{% include "partials/collection-list-item.njk" %}
{% endfor %}
</ul>
</main>
{% endblock %}
Is it possible to do what I want to do in some other way than to have multiple template files?
You might be able to get to the collection using computed data, though I'm not sure if you have access to the collections there.
A simpler approach: Set your frontmatter field to the name of the colletion you want to display as a string, then use that to get the collection in the template:
<!-- politics.md -->
---
layout: articles-list.njk
title: Politics
listCollection: 'politics'
---
{# articles-list.njk #}
{% extends "base.njk" %}
{% block main %}
<main>
{{ content | safe }}
<ul class="postlist no-bullets">
{% for item in collections[listCollection] %}
{% include "partials/collection-list-item.njk" %}
{% endfor %}
</ul>
</main>
{% endblock %}
The way I handled this on my blog (https://github.com/cfjedimaster/raymondcamden2020) was like so:
I create a collection of my categories. I do this by using .eleventy.js and eleventyConfig.addCollection. I use some JavaScript to get all of my posts, iterate over each ones list of categories, and creating a unique list. At the end, I've got a collection called categories.
eleventyConfig.addCollection("categories", collection => {
let cats = new Set();
let posts = collection.getFilteredByGlob("_posts/**/*.md");
for(let i=0;i<posts.length;i++) {
for(let x=0;x<posts[i].data.categories.length;x++) {
cats.add(posts[i].data.categories[x].toLowerCase());
}
}
return Array.from(cats).sort();
});
I then made one page, categories.liquid, that is reponsible for generating my category pages. It's rather simple:
---
pagination:
data: collections.categories
size: 1
alias: cat
permalink: "categories/{{ cat | myEscape }}/"
layout: category
renderData:
title: "{{ cat }}"
---
Notice I use cat for each item in the category and notice I've got nothing else in here. All the layout is done in the category layout page.
Here's the top portion of my category page:
---
layout: default
---
{% assign posts = collections.posts | getByCategory: cat %}
So given I've got a collection of posts where each post has N categories assigned to it, I wrote a custom filter that just reduces this down to a post that has cat as one of it's categories.
S the end result is N category pages filled posts assigned to that category.

How to pass a limit parameter to an include using Jekyll's Liquid

I have a collection of projects on my site that I iterate over using {% for project in site.projects %} and store in an include called projects-list.html.
I would like to include the latest project from this collection on the homepage as a ‘featured’ item – is it possible to include the projects-list but pass in a limit:1 parameter so that only the first project is shown? Based on the Jekyll docs found here, I have tried passing the parameter to the include like this:
{% for project in site.projects limit:{{ include.limit }} %}
and refercing the include like this:
{% include projects-list.html limit=1 %}
but this does not appear to work. Is this a syntax error or am I missing something?
You could try the first
{% assign projectFeatured = site.projects | first %}
{% for projects in site.projects %}
{% include projects-list.html %}
{% endfor %}
{% for projects in projectFeatured %}
{% include projects-list.html %}
{% endfor %}
Though I encourage you to add a featured: True in your post and do something like that:
{% if post.featured == true %}
{% include post.html %}
{% endif %}
https://shopify.github.io/liquid/filters/first/
Below is my current solution (with featured: true) added to post frontmatter. It's really nasty but appears to work for now:
<div class="project-list">
{% if page.layout == "home" %}
{% assign projects = site.projects | where: "featured", "true" %}
{% for project in projects %}
{% include project/project.html %}
{% endfor %}
{% else %}
{% assign projects = site.projects %}
{% for project in projects %}
{% include project/project.html %}
{% endfor %}
{% endif %}
</div>

Jekyll array contains check

I've an array in my _config.yaml. Let's say
exclude_pages: [ "/404.html", "/search.html", "/atom.xml", "/rss.xml", "/index.html", "/sitemap.txt" ]
What I want to do is exclude these pages in the pages loop of site.pages. So following is the code that I'm trying.
{% for entry in site.pages %}
{% if site.exclude_pages contains entry.url %}
<!-- Do Nothing -->
{% else %}
<!-- Show Page -->
{% endif %}
{% endfor %}
But somehow it is not happening. All the pages are being ignored in this code.
Any idea what I'm missing here ?
Try :
exclude_pages: [ "index.html", "anyfolder/index.html" ]
Then loop with entry.path not entry.url:
{% for entry in site.pages %}
{% if site.exclude_pages contains entry.path %}
<!-- Do Nothing -->
{% else %}
<!-- Show Page -->
{% endif %}
{% endfor %}
According to template docs you my try to use Where Expression with contains and unless.
{{ assign entries = site.pages | where_exp:"item", "unless item.url contains site.exclude_pages" }}
{% for entry in entries %}
<!-- Show Page -->
{% endfor %}
See sample how a live code works to generate feed.json in my github site.

Why doesn't this Jekyll Liquid where filter filter?

I am trying to output a list of blog posts for a certain author. I tried this where Jekyll filter:
{% for post in (site.posts | where:"author", "mike") %}
{{ post.title }}
{% endfor %}
But it outputs every post. I'm not clear what I'm doing wrong.
Supposing that your post author is in your front matter, like this :
---
author: toto
...
---
If you want two last post by author == toto, just do :
{% assign counter = 0 %}
{% assign maxPostCount = 2 %}
<ul>
{% for post in site.posts %}
{% if post.author == 'toto' and counter < maxPostCount %}
{% assign counter=counter | plus:1 %}
<li>{{ counter }} - {{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
Et hop !
EDIT :
And another solution using the where filter instead of the if clause :
{% assign posts = site.posts | where: "author", "toto" %}
{% assign counter2 = 0 %}
{% assign maxPostCount2 = 3 %}
<ul>
{% for post in posts %}
{% if counter2 < maxPostCount2 %}
{% assign counter2=counter2 | plus:1 %}
<li>{{ counter2 }} - {{ post.title }}</li>
{% endif %}
{% endfor %}
</ul>
RE-EDIT: Justin is right I don't need my two vars (counter2 and maxPostCount2), I can use Liquid for loop limit:n option.
{% assign posts = site.posts | where: "author", "toto" %}
<ul>
{% for post in posts limit:3 %}
<Ol>{{ post.title }}</ol>
{% endfor %}
</ul>
Better !
You need to do an assign first for the filtered items
{% assign posts = site.posts | where:"author", "mike" %}
{% for post in posts %}
{{ post.title }}
{% endfor %}
It seems filters are to be used only inside output tags (those surrounded by {{ and }}. Which mean you could use something like :
{{ site.posts | where "author", "mike" }}
But you can't use it the way you're doing.
Source: liquid documentation on Filters

Filter site.related_posts in Jekyll

I am very new to Jekyll and Ruby (yet, very excited).
Without using a plugin, I am trying to find a way to filter the site.related_posts.
For example, I am reading the post with title Foo and categories A, B.
The site contains in total 3 posts:
Foo (Categories: A, B)
Bar (Categories: A, C, D)
Zoo (Categories: B, F)
By the default, in Jekyll we do this:
{% for post in site.related_posts limit:5 %}
{% endfor %}
However, the above code returns all the (3) posts.
A post contains many categories, so categories should be an array.
How can I modify the code and return only those whose categories intersect with the current post's categories?
(In this example, I would like the code to return only Foo and Zoo.)
I don't have the ability to test this right now, but something like this will work given Liquid's limited syntax:
{% for post in site.related_posts limit:5 %}
{% assign match = false %}
{% for category in post.categories %}
{% if page.categories contains category %}
{% assign match = true %}
{% endif %}
{% endfor %}
{% if match %}
<li>{{ post.title }}</li>
{% endif %}
{% endfor %}
Make sure each post has a category in the YAML front matter, then add this to where you would like to show the post relating CATEGORY_NAME:
{% for post in site.categories.CATEGORY_NAME %}
<li>
<a href="{{ post.url }}">
<img src="{{ post.thumbnail }}">
<p>{{ post.excerpt }}</p>
</a>
</li>
{% endfor %}

Resources