This document contains some proposals for the remaining template features.
The macro directive allows you to define some sort of function, and can be compared with a named template in XSLT.
<ktl:macro name="my-macro-name">
<ktl:parameter name="foo" [value="defaultValue"]/>
<div>
<p>I really like ${foo} !</p>
</div>
</ktl:macro>
<ktl:callMacro name="my-macro-name"> <ktl:parameter name="foo" value="bar"/> </callMacro>
You can define the macro's anywhere in the template, it is allowed to "call" a macro that is defined later in the template.
A macro definition may contain a call to another template.
Recursion is not allowed - an error will be thrown if recursion is detected.
put macro's in a namespace ? (cfr. jxtemplate)
the following template
<root>
<ul>
<ktl:callMacro name="tuple">
<ktl:parameter name="x" value="10" />
<ktl:parameter name="y" value="20" />
</ktl:callMacro>
</ul>
<ktl:macro name="tuple">
<ktl:parameter name="x" />
<ktl:parameter name="y" value="0" />
<ktl:parameter name="z" value="0" />
<li>(${x},${y},${z})</li>
</ktl:macro>
</root>
results in
<root>
<ul>
<li>(10,20,0)</li>
</ul>
</root>
The import & include directives provide XSLT-alike support for importing / including the contents of one template into another.
<ktl:import src="other-template" />
<ktl:include src="other-template" />
The import and include directives can be used anywhere in the template. This is to support the wireframing where we want to work with snippets of template defined in external files. While not immediately a designed use-case, this means an include/import could e.g. occur in a for-loop.
The src attribute can contain expressions, therefore the src should be evaluated at runtime and thus the actual included/imported template cannot be determined until runtime (= the include/import cannot occur at compile time). Note that the included/imported templates can still be compiled and cached (though only starting from their first use by the executed including templating), it is only the lookup which is dynamic.
Each compiled template has a map of macro name to implementation mappings. The actual runtime macro-mappings are held in the execution context, upon each include or import the macro mappings of the compiled template are merged with those in the execution context (for includes: overwrite, for imports: aanvullen).
An imported template has lower precedence than the importing template: local macro's and var assignments overrule the ones in the imported template.
An included template has the same precedence as the including template: included macro's and var assignments can overrule when include is after the local declaration.
Two questions raise related to variables and includes/imports:
The second question is inspired by XSLT, however we can't really draw this parallel, because the situation is quite different:
As an alternative solution, templates designed for use as include/import can use the new 'overwrite' attribute of ktl:variable. If we would like a real XSLT equivalent, we could introduce constants.
Proposal: we do nothing around this, we'll see if the need arises.
Variables in the template language are scoped, each start-element, for-each, ... pushes a new set of variables on the stack.
If we assume an include/import also start a new scope (actually, since every template has a root element, this is already happening automatically), none of the variables in the include/import would be added to the scope containing the include/import.
If we do not start a new scope, the include/import might unexpectedly overwrite/hide some of the variables in the current template.
Why would we want variables from an include/import to survive into the current scope? Reuse of a common set of variable definitions in various templates.
Again comparing to XSLT, the Kauri template situation is somewhat different, since:
Proposal: undecided yet, maybe we should have an explicit option <ktl:insert src="..." inherit-variables="..."/>, which would inherit the variables of the top-level scope of the imported stylesheet.
Another way of looking at this problem is that a template as a whole is a callable function (just like the macros are), but which lack the possibility to return a value. The 'inherit-variables' is like a bizarre way of saying 'returns these variables as result of the execution of the function'.
So maybe we should rather have a <ktl:return variable="..."/> and a <ktl:include src="..." assign="..."/>, and to allow to return composite values, we could introduce map variables: <ktl:variable name="foo.bar" value="..."/>.
An alternative solution to the problem of not being able to return values is to have global variables, and to avoid too much complexity, limit them to being constants, like in XSLT. This approach provides a practical solution while avoiding the template language to take over too much programming concepts (though personally I don't see this as a problem).
Anyway, I'm still undecided about all this. I'm leaning towards do-nothing-for-now and introduce-global-constants when the need arises.
TODO
The goal of this feature is to be able to split of the common layout of multiple pages to one shared base template, to avoid repetition.
This feature borrows from:
Disclaimer: many of the ideas below are stolen from here and here.
Basic terminology:
Proposed feature set:
A normal HTML/XML page with block elements in it (t is the template language namespace)
.... <t:block name="content"> [ default content for when block is not overridden] </t:block> ...
A normal HTML/XML page with a t:extends attribute on the root element, and blocks to override the block-regions from the base template.
All content outside of the blocks will not be used.
It is not an error to have blocks which do not exist in the base template, these will be ignored too.
Note that the child template can have html, body, and other tags which facilitate viewing or editing the child template as an individual page.
<html t:extends="base.html">
<t:block name="content">
....
</t:block>
<t:block name="navigation">
<t:superBlock/>
</t:block>
</html>