Closed NocturnalSolutions closed 2 years ago
@NocturnalSolutions I have a solution for that but I'm not sure it's a right way to use blocks. In your example imagine that you will change {% block pageTitle %}Hello!{% endblock %}
to {% block pageTitle %}{{ block.super }}Hello!{% endblock %}
. This should render the content of this block from layout.stencil
, but there are two of them and they may be different, what we should pick?
If I understand correctly in your case you are using blocks as a placeholders for something that is defined in extended templates. But I think blocks were originally designed to support another use case - extending their content in extended templates.
@kylef what do you think about that? Here is the change that will fix this issue. Here I'm pushing back already rendered node from extended template so that it can be used again by base template. All tests are passing but maybe we are missing some edge case where it can lead to infinite recursion.
diff --git a/Sources/Inheritence.swift b/Sources/Inheritence.swift
index b9bf87a..4f37022 100644
--- a/Sources/Inheritence.swift
+++ b/Sources/Inheritence.swift
@@ -129,6 +129,7 @@ class BlockNode : NodeType {
func render(_ context: Context) throws -> String {
if let blockContext = context[BlockContext.contextKey] as? BlockContext, let node = blockContext.pop(name) {
+ defer { blockContext.push(node, forKey: name) }
let newContext: [String: Any] = [
BlockContext.contextKey: blockContext,
"block": ["super": try self.render(context)]
Actually tests do fail with this change, so I'm even less sure about it.
Jinja2 solves this this way:
If you want to print a block multiple times, you can, however, use the special self variable and call the block with that name:
<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}
We can do something similar, i.e. by storing all blocks in blocks
context variable and make them resolvable. @kylef what do you think?
Requiring new syntax just for printing a block a second time, while better than nothing, still strikes me as odd, though. Why can't I just use the same syntax in both places? Am I really that odd for wanting/expecting this?
Aside from the example I give in the OP of repeating a title both in <title>
and in a page header, I can think of another example where you might want to use a block twice on a page; you have a pager ("< Prev 1 2 3 4 Next >") that you want to display both at the top and bottom of a very large page.
Yes, I can work around this by using variables, but there are cases where that would clearly break the concept of code/design separation I'm supposed to gain by using a template engine.
Perhaps there are other workarounds I could use that I haven't discovered yet, but what I'm hoping for in the OP seems to me to be the simplest solution from the perspective of the template author.
@NocturnalSolutions I tried to explain in my previous comment why the same syntax can't be used in the way you want it, it creates ambiguity in inheritance. The syntax that is used to define a block can't be the same as syntax used to call this block later, the same way as syntax for defining and calling functions are different in programming languages. I didn't look at it close enough yet, just came across it going through Jinja2 docs, but it seems that using separate syntax is a good solution for this problem. If @kylef will agree I'll be glad to try to implement this in one or another way.
@NocturnalSolutions Doesn't a variable make more sense here?
<!doctype html>
<html>
<head>
<title>My Music Collection: {{ pageTitle }}</title>
</head>
<body>
<h1>My Music Collection</h1>
<h2>{{ pageTitle }}</h2>
{% block pageContent %}{% endblock %}
</body>
</html>
The only difference is that you specify the value in your rendering context, not on the page itself. I do something like that in https://github.com/svanimpe/swift-blog.
Sure, that works in that case. But what about the more complex example I put in my OP of a pager you want to appear at both the top and bottom of a page?
I suppose as a workaround you could render a template to a string that you then use as a variable. I still think just being able to reuse blocks would be a more pleasant solution, though.
In your OP, the content of block pageTitle
is just some text, which is why I suggested a variable.
If you want an entire block of HTML, you could try an include tag? I think that's the only way you can include the same snippet multiple times.
I view blocks as something similar to method overrides with subclassing. That is: you define a default in the parent, and optionally override it in a child. So not simply reusable blocks. I think that's what the include tag is for.
layout.stencil:
hello.stencil:
So my ideal result would be:
I think this is a natural way to have a "subtitle" that appears both in the page content and in the
<title>
tag, but it doesn't seem to work - the secondpageTitle
block isn't filled in. So the result looks like:Not sure if this counts as a bug. If not, please consider it a feature request.