Defining your own directives

Page Contents

As far as template authors are concerned, user-defined directives can be defined using the macro directive. Java programmers who want to implement directives in Java Language, rather than in a template, should use freemarker.template.TemplateDirectiveModel (see more here...).

Basics

A macro is a template fragment associated with a variable. You can use that variable in your template as a user-defined directive, so it helps in repetitive tasks. For example, this creates a macro variable that prints a big ``Hello Joe!'':

<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>  

The macro directive itself does not print anything; it just creates the macro variable, so there will be a variable called greet. Things between the <#macro greet> and </#macro> (called macro definition body) will be executed only when you use the variable as directive. You use user-defined directives by writing @ instead of # in the FTL tag. Use the variable name as the directive name. Also, the end-tag for user-defined directives is mandatory. So you use greet like this:

<@greet></@greet>  

But since <anything></anything> is equivalent with <anything/> you should use this shorter form (that is familiar for you if you know XML):

<@greet/>  

This will print:

  <font size="+2">Hello Joe!</font>
    

But macros can do much more, since the thing between <#macro ...> and </#macro> is a template fragment, thus it can contain interpolations (${...}) and FTL tags (e.g. <#if ...>...</#if>).

Note

Programmers will say on <@...> that you call the macro.

Parameters

Let's improve the greet macro so it can use arbitrary name, not only ``Joe''. For this purpose you can use parameters. You define the parameters after the name of the macro in the macro directive. Here we define one parameter for the greet macro, person:

<#macro greet person>
  <font size="+2">Hello ${person}!</font>
</#macro>  

and then you can use this macro as:

<@greet person="Fred"/> and <@greet person="Batman"/>  

which is similar to HTML syntax. This will print:

  <font size="+2">Hello Fred!</font>
 and   <font size="+2">Hello Batman!</font>
   

As you can see, the actual value of the macro parameter is accessible in the macro definition body as a variable (person). As with predefined directives, the value of a parameter (the right side of =) is an FTL expression. Thus, unlike with HTML, the quotation marks around "Fred" and "Batman" are not optional. <@greet person=Fred/> would mean that you use the value of variable Fred for the person parameter, rather than the string "Fred". Of course parameter value need not be a string, it can be number, boolean, hash, sequence, etc., also you can use complex expression on the right side of = (e.g. someParam=(price + 50)*1.25).

User-defined directives can have multiple parameters. For example, add a new parameter color:

<#macro greet person color>
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>  

and then you can use this macro like:

<@greet person="Fred" color="black"/>  

The order of parameters is not important, so this is equivalent with the previous:

<@greet color="black" person="Fred"/>  

When you call the macro, you can use only parameters that you have defined in the macro directive (in this case: person and color). So if you try <@greet person="Fred" color="black" background="green"/> then you will get an error, since you haven't mentioned parameter background in the <#macro ...>.

Also, you must give value for all parameters that you have defined for the macro. So if you try <@greet person="Fred"/> then you will get an error, since you forgot to specify the value of color. However, it often happens that you would specify the same value for a parameter in most cases, so you want to specify the value only when you want a different value for it than the usual. This can be achieved if you specify the parameter in the macro directive as param_name=usual_value. For example, you want to use "black" for color if you don't specify value for that parameter when you use the greet directive:

<#macro greet person color="black">
  <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>  

Now <@greet person="Fred"/> is OK, since it is equivalent with <@greet person="Fred" color="black"/>, thus the value of color parameter is known. If you want "red" for color, then you write <@greet person="Fred" color="red"/>, and this value will override the usual value specified with the macro directive, so the value of color parameter will be "red".

Also, it is important to realize that -- according to the already explained FTL expression rules -- someParam=foo and someParam="${foo}" are very different. In the fist case, you use the value of variable foo as the value of the parameter. In the second case, you use a string literal with interpolation, so the value of the parameter will be a string -- in this case, the value of foo rendered to text -- regardless of the type (as number, date, etc.) of foo. Or, another example: someParam=3/4 and someParam="${3/4}" are different. If the directive wants a numerical value for someParam, it will not like the second variation. Do not exchange these.

A very important aspect of macro parameters is that they are local variables. For more information about local variables please read: Defining variables in the template

Nested content

Custom directive can have nested content, similarly as predefined directives like <#if ...>nested content</#if> can have. For example, this creates a macro that draws borders around its nested content:

<#macro border>
  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    <#nested>
  </tr></td></table>
</#macro>  

The <#nested> directive executes the template fragment between the start-tag and end-tags of the directive. So if you do this:

<@border>The bordered text</@border>  

the output will be:

  <table border=4 cellspacing=0 cellpadding=4><tr><td>
    The bordered text
  </td></tr></table>
   

The nested directive can be called for multiple times, for example:

<#macro do_thrice>
  <#nested>
  <#nested>
  <#nested>
</#macro>
<@do_thrice>
  Anything.
</@do_thrice>  

will print:

  Anything.
  Anything.
  Anything.  

If you don't use the nested directive, then the nested content will not be executed. Thus, if you accidentally use the greet directive like this:

<@greet person="Joe">
  Anything.
</@greet>  

then FreeMarker will not see this as an error, and simply prints:

<font size="+2">Hello Joe!</font>  

and the nested content will be ignored, since the greet macro never uses nested directive.

The nested content can be anything that is valid FTL, including other user-defined directives. Thus this is OK:

<@border>
  <ul>
  <@do_thrice>
    <li><@greet person="Joe"/>
  </@do_thrice>
  </ul>
</@border>  

and will print:

  <table border=4 cellspacing=0 cellpadding=4><tr><td>
      <ul>
    <li><font size="+2">Hello Joe!</font>

    <li><font size="+2">Hello Joe!</font>

    <li><font size="+2">Hello Joe!</font>

  </ul>

  </tr></td></table>  

The local variables of a macro are not visible in the nested content. Say, this:

<#macro repeat count>
  <#local y = "test">
  <#list 1..count as x>
    ${y} ${count}/${x}: <#nested>
  </#list>
</#macro>
<@repeat count=3>${y!"?"} ${x!"?"} ${count!"?"}</@repeat>  

will print this:

    test 3/1: ? ? ?
    test 3/2: ? ? ?
    test 3/3: ? ? ?  

because the y, x and count are the local (private) variables of the macro, and are not visible from outside the macro definition. Furthermore, a different set of local variables is used for each macro call, so this will not cause confusion:

<#macro test foo>${foo} (<#nested>) ${foo}</#macro>
<@test foo="A"><@test foo="B"><@test foo="C"/></@test></@test>  

and will print this:

A (B (C () C) B) A  

Macros with loop variables

Predefined directives like list can use so-called loop variables; you should read Defining variables in the template to understand loop variables.

User-defined directives can also have loop variables. For example, let's extend the do_thrice directive of the earlier examples so it exposes the current repetition number as a loop variable. As with the predefined directives (as list) the name of loop variables is given when you call the directive (as foo in <#list foos as foo>...</#list>), while the value of the variables is set by the directive itself.

<#macro do_thrice>
  <#nested 1>
  <#nested 2>
  <#nested 3>
</#macro>
<@do_thrice ; x> <#-- user-defined directive uses ";" instead of "as" -->
  ${x} Anything.
</@do_thrice>  

This will print:

  1 Anything.
  2 Anything.
  3 Anything.
   

The syntactical rule is that you pass the actual value of the loop variable for a certain "loop" (i.e. repetition of the nested content) as the parameter of nested directive (of course the parameter can by arbitrary expression). The name of the loop variable is specified in the user-defined directive open tag (<@...>) after the parameters and a semicolon.

A macro can use more the one loop variable (the order of variables is significant):

<#macro repeat count>
  <#list 1..count as x>
    <#nested x, x/2, x==count>
  </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>  

The output will be:

  1. 0.5
  2. 1
  3. 1.5
  4. 2 Last!
   

It is not a problem if you specify different number of loop variables in the user-defined directive start-tag (that is, after the semicolon) than with the nested directive. If you specify less loop variables after the semicolon, then simply you will not see the last few values that the nested directive provides, since there is no loop variable to hold those values. So these are all OK:

<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>
<@repeat count=4 ; c, halfc>
  ${c}. ${halfc}
</@repeat>
<@repeat count=4>
  Just repeat it...
</@repeat>  

If you specify more variables after the semicolon than with the nested directive, then the last few loop variables will not be created (i.e. will be undefined in the nested content).

More about user-defined directives and macros

Now you may read the relevant parts of the FreeMarker Reference:

You can define methods in FTL as well, see the function directive.

Also, you may interested in namespaces: Namespaces. Namespaces help you to organize and reuse your commonly used macros.

FreeMarker Manual -- For FreeMarker 2.3.20
HTML generated: 2013-06-27 20:54:33 GMT
Edited with XMLMind XML Editor
Here!