Open nhoizey opened 4 years ago
If I recall correctly, in Eleventy, Nunjucks happily passes content through to the specified layout without using extends
if you are just populating the content
. When you have multiple blocks that you wish to pass through to a layout, then you need to use extends
.
At least, this seems to work for me™
I'm struggling to find the documentation I thought had lead me to that conclusion.
@philhawksworth what I see in your aforementioned post.njk
layout is that you have an explicit {% extends … %}
, but no Front Matter and no content outside {% block … %}
s.
So it looks like it's either one or the other:
layout
property
OR{% extends … %}
and a series of {% block … %}
sThe later seems to be the best (or even the only way) to "pass" an HTML fragment (a "block") to the parent (extended) layout.
Oh, looks like @zachleat already answered this is "by design": https://github.com/11ty/eleventy/issues/834#issuecomment-569474008
That's not why I understood when reading the Addendum about existing Templating features, maybe we could rephrase it.
Aha. Yeah, there it is. Nice one @nhoizey!
Default support for blocks would be super nice.
In the mean time, I've set up these tags to recreate similar functionality, maybe it's of help to someone else.
module.exports = function (eleventyConfig) {
const layoutblocks = [];
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.page.layoutblock || {})[name];
});
eleventyConfig.addPairedShortcode('layoutblock', (content, name) {
if (!this.page.layoutblock) this.page.layoutblock = {};
this.page.layoutblock[name] = content;
});
}
Define a layoutblock
block with name foo
like this in the layout template:
{% renderlayoutblock 'foo' %}
Set the contents of layoutblock
with name foo
in a document like this:
{% layoutblock 'foo' %}
Hello World
{% endlayoutblock %}
@rikschennink Thanks for the suggestion! this.page.layoutblock
doesn't work because this.page
is undefined
. And the variable const layoutblocks = []
is obsolete as it's not used. But this worked for me:
module.exports = function (eleventyConfig) {
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.layoutblock || {})[name];
});
eleventyConfig.addPairedShortcode('layoutblock', (content, name) {
if (!this.layoutblock) this.layoutblock = {};
this.layoutblock[name] = content;
});
}
@gluecksmensch good one.
Previous iteration I stored data in const layoutblocks = []
but that didn't work ( obviously :D ) forgot to remove.
I guess with my templates page
is defined, didn't realise this wasn't always the case, nice improvement 👍
@rikschennink @gluecksmensch as we showed before in the thread, you can use Nunjucks' {% extend … %}
directly, without additional shortcode, but you can't use Eleventy's layout
Front Matter with it.
I do this here for example: https://github.com/nhoizey/precious-prana.com/blob/master/src/_includes/layouts/evenement.njk#L2
And here's the base.njk
layout definig blocks:
https://github.com/nhoizey/precious-prana.com/blob/master/src/_includes/layouts/base.njk
11ty newbie here. I hit this same issue, and followed the pattern from @nhoizey, however all the block sections are missing in the generated html.
index.njk:
{% extends "layout.njk" %}
{% block title %} Home page in HTML {% endblock %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>
Welcome to my 11ty website.
</p>
{% include "footer.njk" %}
{% endblock %}
_includes/layout.njk
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<style>
.active { background: yellow }
</style>
{{ header | safe }}
</head>
<body>
<div>
<a href="/" class="{{ 'active' if '/' == page.url }}">Home</a>
<a href="/README" class="{{ 'active' if '/README/' == page.url }}">README</a>
</div>
{{ content | safe }}
</body>
</html>
Generated index.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
.active { background: yellow }
</style>
</head>
<body>
<div>
<a href="/" class="active">Home</a>
<a href="/README" class="">README</a>
</div>
</body>
</html>
Full source code is here: https://github.com/surferjeff/scratch/tree/main/hello-11ty
@surferjeff Looking at @nhoizey's repo, I think you need to define your blocks in your _includes/layout.njk file, similar to this:
- <title>{{ title }}</title>
+ {% block title %}
+ <title>{{ title }}</title>
+ <meta property="og:title" content="{{ title }}" />
+ {% endblock %}
Then you can either set the title
via front matter, or override it by explicitly setting the {% block title %}
block:
{% block title %}
<title>Precious Prana, faire pétiller</title>
<meta property="og:title" content="Precious Prana, faire pétiller" />
{% endblock %}
@surferjeff Try something like this:
<!DOCTYPE html>
<html>
<head>
{% block title %}
<title>{{ title }}</title>
{% endblock %}
<style>
.active { background: yellow }
</style>
{% block header %}
{{ header | safe }}
{% endblock %}
</head>
<body>
<div>
<a href="/" class="{{ 'active' if '/' == page.url }}">Home</a>
<a href="/README" class="{{ 'active' if '/README/' == page.url }}">README</a>
</div>
{% block content %}
{{ content | safe }}
{% endblock %}
</body>
</html>
And…
{% extends "layout.njk" %}
{% block title %}
<title>Home page in HTML</title>
{% endblock %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>Welcome to my 11ty website.</p>
{% include "footer.njk" %}
{% endblock %}
Or set the title
via front matter, if you don't want to set the <title>
tags and override other stuff:
---
title: Override title
---
{% extends "layout.njk" %}
{% block header %}
<meta charset="utf-8" />
{% endblock %}
{% block content %}
<p>Welcome to my 11ty website.</p>
{% include "footer.njk" %}
{% endblock %}
Ah, I see now. Thanks for the quick replies!
This is not well documented. Some starter projects examples use the {% extends ... %}
approach and some use the YML
frontmatter approach. Specially when it comes to choosing between mardown index vs template-engine index. It's realy weird that you can have frontmatter and Pug (e.g) in the same file, but Pug blocks won't be assimilated as they should
. This is basically changing the engines' default functionality and introducing unexpected behaviour without mentioning it.
Default support for blocks would be super nice.
In the mean time, I've set up these tags to recreate similar functionality, maybe it's of help to someone else. [...snip]
(Edit: sorry, @gluecksmensch, it didn't work for me without the .page
. Not sure why...)
Thanks so much @rikschennink
This works for me also 👍
Though for me it was also outputting undefined
where I was using the blocks (or had no content in the block) so I modified it slightly. Hopefully it helps someone:
module.exports = function (eleventyConfig) {
eleventyConfig.addShortcode('renderlayoutblock', function(name) {
return (this.page.layoutblock || {})[name] || '';
});
eleventyConfig.addPairedShortcode('layoutblock', function(content, name) {
if (!this.page.layoutblock) this.page.layoutblock = {};
this.page.layoutblock[name] = content;
return '';
});
}
Usage reminder (thanks again, @rikschennink):
Define a layoutblock
block with name foo
like this in the layout template:
{% renderlayoutblock 'foo' %}
Set the contents of layoutblock
with name foo
in a document like this:
{% layoutblock 'foo' %}
Hello World
{% endlayoutblock %}
Default support for blocks would be super nice. In the mean time, I've set up these tags to recreate similar functionality, maybe it's of help to someone else. [...snip]
(Edit: sorry, @gluecksmensch, it didn't work for me without the
.page
. Not sure why...) Thanks so much @rikschennink This works for me also 👍 Though for me it was also outputtingundefined
where I was using the blocks (or had no content in the block) so I modified it slightly. Hopefully it helps someone:module.exports = function (eleventyConfig) { eleventyConfig.addShortcode('renderlayoutblock', function(name) { return (this.page.layoutblock || {})[name] || ''; }); eleventyConfig.addPairedShortcode('layoutblock', function(content, name) { if (!this.page.layoutblock) this.page.layoutblock = {}; this.page.layoutblock[name] = content; return ''; }); }
Usage reminder (thanks again, @rikschennink): Define a
layoutblock
block with namefoo
like this in the layout template:{% renderlayoutblock 'foo' %}
Set the contents of
layoutblock
with namefoo
in a document like this:{% layoutblock 'foo' %} Hello World {% endlayoutblock %}
This worked for me. Previously, I was also getting "undefined" in the output. Now I don't. Thanks.
Hello, this method using the global this
context worked for me before the 3.0.0 update introducing ESM support, but now export default
sets this
to undefined
. Is there an updated approach to global context after the update?
I'm using Nunjucks layouts, and the
layout
attribute in Front Matter for layout chaining, so that apage.njk
layout uses abase.njk
layout.As I read it, the Addendum about existing Templating features states that “Eleventy’s layout system exists a layer above Nunjucks' Template Inheritance exposed with
{% extends %}
“.So I thought
{% extends %}
was implicit when using layout chaining, but a{% block %}
defined in mypage.njk
layout never arrives intobase.njk
.I tried to add an explicit
{% extends %}
like I saw in Phil Hawksworth's code, but I get thebase.njk
included twice, kind of recursivelyIf I remove the
layout
attribute from the Front Matter of thepage.njk
layout, its content doesn't arrive intobase.njk
anymore.I must be missing something obvious, so how is it supposed to work?
Thanks