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:
But since
<anything></anything>
is equivalent with
<anything/> you
should use this shorter form (that is familiar for you if you know
XML):
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:
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.