7. Velocity Macros

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>

7.1 Velocimacro Arguments

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]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 )