Internal subtemplates, a.k.a. nested/inline (sub)templates #162
Replies: 2 comments 2 replies
-
JStachio has a feature called fragments: https://jstach.io/doc/jstachio/current/apidocs/#mustache_fragments I didn't make it possible yet or maybe I did (my head is in a fog lately w/ covid) but inside the same template one could do The That is normally most use it like: And even if the short form (relative) of just a fragment As far as I'm aware JStachio is the only implementation that has something like this. The reason why blocks are used for fragments often is because JStachio compile time checks. Other implementations would not have this problem. Actually I guess in JStachio one could use a dotted I suppose a special sigil could be added to alleviate the commandeering the semantics of blocks. EDIT also to answer this:
JStachio does the first found. It basically is a preprocess of looking for section-likes with the exact name. |
Beta Was this translation helpful? Give feedback.
-
Another implementation that I forgot supported inline templates (they call them nested): http://trimou.org/doc/latest.html#nested_templates They use the |
Beta Was this translation helpful? Give feedback.
-
This idea was previously discussed in #63 and may be better known as "inline partials" by some. I consider that name a little bit misleading, for reasons I will clarify below.
I bumped the discussion about two years ago with #63 (comment), leading to many new comments. Below, I will attempt to paint the consensus that seemed to come out of that discussion, as well as the choices that are still up to debate. Before I jump into that, however, I want to clear up some terminology.
Terminology
In conversations, the words "partial" and "parent" are often loosely used to refer to an external template that will be rendered in the place of a
{{>partial}}
or{{<parent}}...{{/parent}}
, respectively. This manner of speech is very sloppy! The external template is not inherently a partial or a parent; it is just a template. Every template may be rendered by itself, as a partial, as a parent, or sometimes in one way and sometimes in another.Hence, "partial" and "parent" should be thought of as referring not to a template, but to the way a template is rendered in a particular place. You render a template as a partial or as a parent, but the template itself is just a template. There is no such thing as a "partial template" or a "parent template"; it makes more sense to speak of a "partial tag" or a "parent tag pair".
Another thing to realize is that the spec currently already includes a type of nested template: blocks, with the notation
{{$block}}...{{/block}}
. There are actually two types of blocks to distinguish. A block inside a parent tag pair behaves as a kind of "mobile" subtemplate that we send to external templates. A block outside any parent pair acts as a "landing platform" for such a mobile subtemplate, and also acts as the fallback template in case no mobile subtemplate is sent in from elsewhere.Above, I frequently used the word "external". If I have two template files (or strings), say
resume.mustache
andmotivation.mustache
, then those templates are external to each other. In contrast, ifresume.mustache
contains a block namedentry
, we can say thatentry
is internal toresume.mustache
.Partials and parents enable interactions between external templates, by allowing one to interpolate the other in whole. Blocks enable external templates to exchange subtemplates.
The current proposal does not involve external templates. It is about a way to define subtemplates that are interpolated internally.
Motivation
Suppose that we have a large template where we often refer to people with their roles within an organization. Naively, we might write a template that contains many copies of the following string:
This is obviously a maintenance nightmare. How do we ensure we keep doing this in a consistent way, and what if we actually want to change the pattern everywhere?
A readily available solution is to outfactor the above string to an external template, say
person.mustache
, so we can replace all occurrences in the big template by just{{>person}}
. Let us say that the big template is calledproject.mustache
:The
project
template by itself is nicely maintainable in this way, but there is a different cost to this solution. Theperson
template is tiny, but it still takes up an entire disk block. More importantly, we now need to write configuration to make the Mustache engine aware ofperson
and we need to load it from disk every time we useproject
. We also need two windows or tabs in our text editor in order to be fully able to edit theproject
template. For just two templates, this may not seem like a big deal, but if we repeat this pattern often, we end up with lots of tiny templates and the overhead can be significant, not in the least mentally. Hence, always using external templates for this purpose does not scale well.This is where internal subtemplates come in. Instead of creating a separate
person.mustache
file, we write the following line somewhere insideproject.mustache
:The rest of
project.mustache
can stay exactly the same as before. The notation{{:person}}...{{/person}}
allows us to write{{>person}}
within the same file as if there existed an external template with the nameperson.mustache
and the content...
.Of course, if the
person
template is used not only inproject.mustache
but also incontract.mustache
, then the only way to keep a single source of truth is by makingperson
an external template. Hence, internal subtemplates are intended for small templates that are used in only one other template. The original proposal in #63 was prompted by frequent occurrence of this situation.Consensus
Using
:
as the sigil seems fairly uncontroversial. It suggests a definition.=
would also suggest a definition and has also been suggested, but it is already taken by the Change Delimiter tag. Originally, the feature was described with*
as the sigil, but its relation with a definition is less clear and it could be confused with dynamic names.Since internal subtemplates are interpolated using partial or parent tags, they necessarily live in the same namespace as external templates.
If we have an internal
{{:person}}...{{/person}}
subtemplate and there also exists an externalperson.mustache
, then most people will probably agree that the internal subtemplate should be used rather than the external template. The template author wrote the internal subtemplate in order to use it; the external template might have been written by someone else.In implementations that support inheritance, an internal subtemplate can also be used with parents and blocks. For example, if we add a block to our
person
subtemplate,we can override it elsewhere in
project.mustache
with parent notation,So we may sometimes get
Cynthia, connaisseur of cheese
instead ofCynthia, security manager at Initech
. The default case is still also available through{{>person}}
.If an internal subtemplate and its place of interpolation both contain whitespace, like in the following example,
most will probably agree that the above should be equivalent to the following:
and specifying this could likely borrow from #131.
Open questions
Where in the containing template is an internal subtemplate available? Most will probably expect a subtemplate to be available on the next line. But is it also available on the previous line? If it appears inside a section, is it local to that section or is it also available outside of it?
Personlly, I would prefer that you can use the subtemplate anywhere, regardless of where in the file it is defined. This is analogous to the unorderedness of external templates. I also think this would be the easiest to implement in Wontache.
May multiple subtemplates within a single template have the same name? If yes, then the next question is how to determine which candidate should be used where. @gasche has suggested section-based scoping rules. @groue has suggested that the last definition wins.
Personally, I would be inclined to disallow it, and leave it implementation-defined what happens when a template author breaks this rule. I see no need for masking or anything like that; the author is in full control of the template, so if they want to have multiple flavors of a subtemplate, they can just choose a different name for each flavor. Also, you cannot have multiple external templates with the same name, either.
Can subtemplates have private sub-subtemplates? In other words, assuming that there is no external
weird.mustache
, should the following template renderponytail
or only whitespace?I think this should be left unspecified. My reason is that I do not want to encourage nesting subtemplates.
strange
would be able to useweird
regardless of whether it is nested:Hiding information about
weird
from the rest of the template seems extremely over-engineered to me, especially since most subtemplates will be very small.Can internal subtemplates bleed into partials? In other words, with the following set of templates, should the
desert
template renderorange
orpear
?I see two independent strong reasons to render
orange
: (1) an internal subtemplate should remain private to the containing template and (2) renderingpear
would be extremely surprising for the author ofbasket.mustache
. However, I do think the following version should renderpear
, because the override is explicit on both sides:How to name this? "Inline partial" is inaccurate, but "internal subtemplate" is a mouthful. Maybe "inline template" would be a viable compromise?
Beta Was this translation helpful? Give feedback.
All reactions