The #macro
directive allows you to name a section
of a VTL template and re-insert it multiple times into the template.
Velocity macros (or short Velocimacros) can be used
in many different scenarios.
In the following example, a Velocimacro is created to save keystrokes and minimizing typographic errors:
#macro( d ) <tr><td></td></tr> #end
This example defines a Velocimacro called d
. It
now can be uses as any other VTL directive:
#d()
When this template is rendered, Velocity replaces
#d()
with the HTML table row containing a single, empty
cell defined above. Using a macro in a template is called macro
invocation.
Velocimacros start with the #macro
directive and
are ended with #end
. The enclosed block is the
macro body. Whenever a Velocimacro is invoked, its
body is inserted in the template. It is rendered at every invokation
through the templating engine, with the invocation arguments replacing the
arguments in the macro definition.
A Velocimacro can take any number of arguments including zero, but when it is invoked, it must be called with exactly the same number of arguments with which it was defined.
Here is a Velocimacro that takes two arguments, a color and a list of objects:
#macro( tablerows $color $values ) #foreach( $value in $values ) <tr><td bgcolor=$color>$value</td></tr> #end #end #set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] ) #set( $color = "blue" ) <table> #tablerows( $color $greatlakes ) </table>
The tablerows
macro takes exactly two arguments.
The first argument takes the place of $color
, and the
second argument takes the place of $values
. Anything
that can be put into a VTL template can go into the body of a
Velocimacro.
Notice that $greatlakes
takes the place of
$values
. When this template is rendered, the following
output is generated:
<table> <tr><td bgcolor="blue">Superior</td></tr> <tr><td bgcolor="blue">Michigan</td></tr> <tr><td bgcolor="blue">Huron</td></tr> <tr><td bgcolor="blue">Erie</td></tr> <tr><td bgcolor="blue">Ontario</td></tr> </table>
In the example above, the #tablerows
macro was
defined inline. Velocimacros can be defined
inline or global. An inline
macro is only visible in the template where it is defined, a global macro
is accessible from all templates. Global definitions must be done in a a
Velocimacro template library, which is a template file that contains the
macros and they must be referenced explicitly through the Velocity
configuration. Please see the Velocity reference guide for more
information on how to define and load a Macro library.
If the #tablerows($color $values)
Velocimacro is
defined a Velocimacro template library, it could be in any template. In
the fruit store it could be used to list fruits available:
#set( $fruits = ["apple", "pear", "orange", "strawberry"] ) #set($cellbgcol = "white" ) <table> #tablerows( $cellbgcol $fruits ) </table>
When rendering this template, Velocitywould find the
#tablerows
Velocimacro in a template library (defined
in the Velocity configuration) and generate the following output:
<table> <tr><td bgcolor="white">apple</td></tr> <tr><td bgcolor="white">pear</td></tr> <tr><td bgcolor="white">orange</td></tr> <tr><td bgcolor="white">strawberry</td></tr> </table>
Velocimacros can take as arguments any of the following VTL elements:
Reference : anything that starts with '$'
String literal : something like "$foo" or 'hello'
Number literal : 1, 2 etc
IntegerRange : [ 1..2] or [$foo .. $bar]
ObjectArray : [ "a", "b", "c"]
The words true and false: boolean values for true and false
When passing references as arguments to Velocimacros, please note that references are passed by name. This means that their value is evaluated each time it is used inside the Velocimacro. This feature allows you to pass references with method calls and have the method called at each use:
#macro( callme $a ) $a $a $a #end #callme( $foo.bar() )
results in the method bar()
of the reference
$foo
being called 3 times.
![]() | Caution |
---|---|
If you are used to the behaviour of method or procedure calls in programming languages like Java or C, please be sure to fully understand the implications of the passing by name of references. Unlike these programming languages, the reference is not evaluated at macro invocation time but at every occurence inside the macro. Don't treat macros as procedures or methods! |
The easiest way to avoid any gotchas is not passing any stateful object into a Velocimacro. This can be done by assigning the result of a method invocation to a new reference:
#set( $myval = $foo.bar() ) #callme( $myval )