Defining your own directives - FreeMarker Manual

- Home
- Manual
- Java API

- Contribute
- Report a Bug
- Download

Manual
Previous Next

# Defining your own directives

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:

- user-defined directive call
- `macro` directive

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.

Previous Next