Nunjucks, blocks defined within "include" partial file ignored by extend - include

Here is my use case
I have a file that pulls pieces together (holy-grail.njs) that then is extended by the actual page I want (single.njs). Problem is that the blocks defined in included files are ignored by the actual page template. You can see the extended sidebar text is not appearing in screenshot <p>Here is some additional content for the sidebar extended in</p>
Just for grins I moved that block code from sidebar.njs to holy-grail.njs and yes it is extended by single.njs so it is indeed as I suspected.
Is the a bug?, a non-feature? How about a work around? Or will Jade do what I want? I assumed that "included" files were just included as in before further processing but maybe they are processed then included? Without being able to do this my whole groovy way of organizing my template pieces/partials and then extending/customizing the content will be a no go.
this same question was asked three years ago and no one answered
Blocks in included files not being filled by extended templates
single.njs
{% extends "layouts/holy-grail.njs" %}
{% block sidebar %}
<p>Here is some additional content for the sidebar extended in</p>
{% endblock %}
{% block article %}
{% raw %}
{{ Hugo code here to grab the article title and content }}
{% endraw %}
{% endblock %}
holy-grail.njs
{% set reg = "regions/" %}
{% set cmp = "components/" %}
{% include "regions/head.njs" %}
<body class='page'>
{% include reg + "header.njs" %}
<div class="rollover-wrapper">
{% include reg + "topbar.njs" %}
<main>
{% include reg + "sidebar.njs" %}
<section id="content">
{% block article %}
{# This is where one melds in article content #}
{% endblock %}
</section>
</main>
{% include reg + "footer.njs" %}
</div>
{% include cmp + "javascripts.njs" %}
</body>
sidebar.njs
<aside id="sidebar" class="sidebar">
<p> some sidebar content set in the sidebar.njs file </p>
{% block sidebar %}
{# This is where one melds in more sidebar content #}
{% endblock %}
</aside>

This from one of the maintainers of Nunjucks
Your guess is correct: includes operate at a "higher level" than
template inheritance. So an included template can have blocks, and can
have its own "extends" tag, and a totally separate template
inheritance hierarchy, but its blocks don't in any way interact with
the blocks of the including template's inheritance hierarchy.
So the solution I came up with is to use another tool (gulp plugin) to pre-assemble the partials then run them through nunjucks like this.
N.B my preprocessor file uses extension *.pnjs and outputs to a corresponding *.njs file which then is processed/extended in the single.njs.
var merge = require('gulp-file-include')
var rename = require("gulp-rename");
gulp.task('html:pre', function() {
gulp.src(['assets/html/nunjucks/layouts/*.pnjs'])
.pipe(merge({
prefix: '##',
basepath: 'assets/html/nunjucks'
}))
.pipe(rename({extname: ".njs"}))
.pipe(gulp.dest('assets/html/nunjucks/layouts/'));
});
var nunjucks = require('gulp-nunjucks-render');
gulp.task('html:njs',['html:pre'], function() {
gulp.src('assets/html/nunjucks/*.njs')
.pipe(nunjucks({ path: ['assets/html/nunjucks'] // String or Array
}))
.on('error', console.log)
.pipe(gulp.dest('./builds/dev/'));
});
and then you'd have to change your include syntax to that supported by gulp-file-include
like this.
##include('regions/head.njs')
<body class='page'>
##include('regions/header.njs')
<div class="rollover-wrapper">
##include('regions/topbar.njs')
<main>
##include('regions/sidebar.njs')
<section id="content">
{% block article %}
{# This is where one melds in article content #}
{% endblock %}
</section>
</main>
##include('regions/footer.njs')
</div>
##include('components/javascripts.njs')
</body>

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 filter Taxonomies using Rust-based Zola / Tera?

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.

Create Jekyll link from YAML array

I have a YAML array in a file called navigation.yml as follows:
docs:
- title: Home
url: index.md
id: index
- title: Support
url: support.html
id: support
- title: About
url: about.md
id: about
I am creating a navigation bar as follows:
<section id="navigation" class="clearfix">
{% for item in site.data.navigation.docs %}
<span>{{ item.title }}</span>
{% endfor %}
</section>
What should I put in place of index.md to get the item.url that I want from the YAML file.
I am totally new to GitHub Pages, YAML, and Jekyll.
At the moment, the link tag doesn't seem to support variables.
There's a pull request trying to change this, but it has not been merged into the main Jekyll repo yet.
So if you want to do this now, you need to use some tricks.
The solution suggested by flyx in his comment (replace {% link index.md %} by {{ item.url }}) basically works, but shows the original filename written in the data file.
⇒ If index.md is automatically renamed to index.html while rendering the site, your link won't work anymore.
(or if support.html becomes support/index.html)
That's probably why you wanted to use the link tag instead.
Without using the link tag, you need to loop your data file, loop through all pages to find the respective page, and show that page's actual URL in your link:
<section id="navigation" class="clearfix">
{% for item in site.data.navigation.docs %}
{% for page in site.pages %}
{% if page.path == item.url %}
<span>{{ item.title }}</span>
{% endif %}
{% endfor %}
{% endfor %}
</section>
This even takes stuff like explicitly set permalinks (permalink: /whatever/ in the page's front matter) into account.

How do I add a link to an external site the main navbar in CKAN

I know that I can customize the set of navigation links at the top of the page in the standard template by overriding the header_site_navigation_tabs block e.g. as in the [datahub.io customization](https://github.com/okfn/ckanext-datahub/blob/3d64748fc1f3c4499780b199e971a5929ba69315/ckanext/datahub/templates/header.html#L9
)
{% block header_site_navigation_tabs %}
{{ h.build_nav_main(
('search', _('Datasets')),
('organizations_index', _('Organizations')),
('about', _('About'))
) }}
{% endblock %}
However, I want to add a link to this list to an external website? Can I just do (see extra entry at the end):
{% block header_site_navigation_tabs %}
{{ h.build_nav_main(
('search', _('Datasets')),
('organizations_index', _('Organizations')),
('about', _('About'))
('http://blog.datahub.io/', 'Blog')
) }}
{% endblock %}
No, you can't do that. The helper method looks for routes declared internally (config['routes.named_routes']). You can, however, simply add a li element, so the whole block would look like this
{% block header_site_navigation %}
<nav class="section navigation">
<ul class="nav nav-pills">
{% block header_site_navigation_tabs %}
{{ h.build_nav_main(
('search', _('Datasets')),
('organizations_index', _('Organizations')),
('about', _('About'))
) }}
<li>Blog</li>
{% endblock %}
</ul>
</nav>
{% endblock %}

Twig include finding set when in a file.

I am trying to use twig / include to pull content from two files where the second file passes as a variable the 'set' content from the first. The problem is that the 'set' content cannot be seen when it comes from a file. As an example, this works
{% set localContent %}
<div> someContent </div>
{% endset %}
{% include 'MyBundle:Templates:some.html.twig' %}
{% include 'MyBundle:Templates:main.html.twig' with {
'includedContent': localContent,
} %}
where main.html.twig is simply:
{% block form_row %}
<div> mainContent </div>
{{includedContent}}
{% endblock form_row %}
and some.html.twig contains:
{% set fileContent %}
<div> someContent </div>
{% endset %}
When I change the includedContent variable to fileContent which is defined in a file I get an exception that indicates fileContent cannot be located.
Is what I am trying to do possible ?
Can anyone help me, I would like to get this application finished before the world ends in a few days :-).
I found another mechanism to accomplish the same task. If I pass the include path into the main twig file and use that to access the fileContent everything works as expected.

Resources