Open thekid opened 8 months ago
To avoid naming clashes, one idea could be to use variables prefixed by an @
for passing
home.handlebars:
{{#> layout @title="My app"}}
...
{{/layout}}
layout.handlebars:
<title>{{#with @title}}{{.}}{{else}}Default title{{/with}}</title>
This notation has no special meaning here but would mimic that of https://handlebarsjs.com/api-reference/data-variables.html#data-variables - and decrease the risk of clashing with context variables or helpers.
To shorten the with-else construct, we could make use of the ^
form and simply use sections instead of the with helper as follows:
- {{#with title}}{{.}}{{else}}Default title{{/with}}
+ {{#title}}{{.}}{{^}}Default title{{/title}}
Following this, a short syntax that could be introduced might be:
{{title ^ "Default title"}}
(much like Laravel's {{ $name or 'Guest' }}
, see also https://github.com/handlebars-lang/handlebars.js/issues/1146)
This currently parses into a VariableNode(name: title, options: [Lookup(^), Quoted("Default Title")])
- we could add this without changing the parser itself - we would only overwrite the write method and have it react to the ^
operator (*and possibly others).
However, this would deviate from the official Handlebars language (where the above is a parse error!) and would increase the learning curve.
Another option without the need for modifying the parser would be a coalesce
helper which:
- {{#with title}}{{.}}{{else}}Default title{{/with}}
+ {{coalesce title 'Default title'}}
This helper is trivial to implement and comes in handy in a number of situations.
Solution
The typical layout makes use of inline partials as follows:
layout.handlebars:
home.handlebars:
Templates using the layout must specify all inline templates, or else rendering will throw an exception. To make specifying a certain inline template optional, we can use partial blocks as follows:
Another option is to use partial parameters, which works best for plain strings:
The home template would then pass this parameter via
{{#> layout title="My title"}}
. *On a side note, we could create a helper to shorten the with-else syntax to something along the lines of{{coalesce title "Default title"}}
.So basically, we can mimic the following:
Limitations
However, this does not work for more than one level. For example, we cannot have some common navigation which is inherited for all pages, but then replaced by a specific one inside a page template: The common navigation would always be used due to how inline templates' scoping works.
One solution to this is to call optional templates in the common layer:
This would render the Default navigation if the template does not specify a site-nav inline partial, its contents otherwise.
See also
{{!layout ...}}
and supports multiple layers of nesting.{{#block ...}
{{#extend "layout" foo="bar"}}