When replicating the concepts of another template engine, there are always moments when you put off working on a particular concept. This rarely has anything to do with the complexity of the concept itself, but often stems from a personal aversion. For FreshMarker, this has meant that some concepts have been implemented late or completely reworked. Some concepts are still awaiting implementation. One of these concepts is the parameters of the nested directive, which will be implemented for FreshMarker 2.7.0.
FreshMarker does not yet support nested directives with parameters. These are required within a macro directive to customize its content via the user directive. Before we examine this process in more detail, let’s review the current implementation and its capabilities.
A macro directive allows you to define the semantics of a user directive within the FreshMarker Template Language.
<#macro copyright from>
/*
* Copyright © ${from}-${.now?string('yyyy')} Jens Kaiser
*
* All Rights reserved.
*/
</#macro>
The body of a macro directive defines the output of a user directive and, as shown here, can consist of text and interpolations. However, other directives are also possible, as we will see in later examples. The two parameters, copyright and from, in the macro directive define the name of the user directive and the parameters of the user directive. In this case, the user directive has only one parameter, but multiple parameters can also be used.
<@copyright from="2022"/>
The macro can be called using a user directive named copyright. To do this, you must also specify the from parameter. The output of the user directive is then the following text.
/* * Copyright © 2022-2026 Jens Kaiser * * All Rights reserved. */
The macro’s parameters can also have a default value. In the following example, the macro has a second parameter, author, with the default value Jens Kaiser.
<#macro copyright from author="Jens Kaiser">
/*
* Copyright © ${from}-${.now?string('yyyy')} ${author}
*
* All rights reserved.
*/
</#macro>
<@copyright from="1968"/>
<@copyright from="2025" author="Anonymous"/>
If the user directive is specified as shown above, the output does not change. If the author parameter is specified with a different value, as shown above, the output changes.
/* * Copyright © 2025-2026 Anonymous * * All Rights reserved. */
The nested directive provides even more options for customizing the output. It inserts the contents of the user directive—that is, everything between its start and end tags—into the output. The following example uses the nested directive to modify the second line of comment text.
<#macro copyright from author="Jens Kaiser">
/*
* Copyright © ${from}-${.now?string('yyyy')} ${author}
*
* <#nested>
*/
</#macro>
<@copyright from="1968">All Rights reserved.</@copyright>
<@copyright from="1968">Licensed under the Apache License, Version 2.0</@copyright>
In the first case, the output remains unchanged, since the nested directive is replaced by the text All Rights Reserved. In the second case, we get the following output.
/* * Copyright © 1968-2026 Jens Kaiser * * Licensed under the Apache License, Version 2.0 */
So far, the nested directive can be specified multiple times within the macro, and each call is replaced by the content of the user directive. The following example generates four lines of the text ▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼.
<#macro multiple count=4>
<#list 1..count as i>
${i} <#nested>
</#list>
</#macro>
<@multiple>▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼▲▼</@multiple>
While this looks nice, it highlights the current limitations of nested directives. Multiple nested directives only make sense if the individual directives can produce different outputs—that is, if they are parameterizable.
An example of a parameterizable nested directive is shown below. The member_table macro assists in generating tables of members, including a serial number, the member’s name, and their hometown.
These values from the members list are passed to the nested directive within the list directive.
<#macro member_table members>
<#list members as member with looper>
<#nested looper?roman member.name member.city?upper_case>
</#list>
</#macro>
|===
| # | Name | Email
<@member_table members=list ; number, name, city>
|${number?roman}
|${name}
|${city}
</@member_table>
|===
The three values can be accessed within the user directive. To do this, appropriate names for the three parameters are defined in the user directives start tag after a semicolon. In this example, number, name, and city. Once they have been defined, these names can be used within the user directive to access the parameters. If the parameter names are not defined, the standard mechanism for variables is used. This can lead to incorrect values or, in many cases, to errors due missing values.
|=== | # | Name | Email |I |Jens |BIELEFELD |II |Kay Uwe |HAMBURG |===
The above output can be generated for a members list with two entries.
An implementation of this mechanism now exists and will be included in the next version of FreshMarker. However, this mechanism does not seem particularly clean. The syntax with the semicolon appears to have been added later in Freemarker. In fact, it is the only place in the FreshMarker Template Language where a semicolon is used and there are only two places in Freemarker. Three directives are involved, each of which must be aware of the nested parameters involved, independently of the others. In the macro directive, the values are passed to the nested directive, but neither the existence of the parameters, nor their order, nor even their meaning can be inferred from its signature.
The next post on this topic will reveal how the current solution was implemented.