5.4 Loops - #foreach

#foreach iterates over the elements given in a List or Array element.

Example 5.5. foreach loop example

<ul>
  #foreach( $product in $allProducts )
    <li>$product</li>
  #end
</ul>

Unlike other languages, the Velocity templating language allows only loops where the number of iterations is predetermined. There is no do..while or repeat..until loop in Velocity. This has been a conscious decision to make sure that the rendering process of Velocity does always terminate.[8]

A VTL foreach loop takes two arguments, the first one is the loop variable and the second is an object that can be iterated over. Velocity supports a number of object types in its default configuration:[9]

[Warning]Warning

Using an Iterator or Enumeration object directly in a template has the side effect that the loop cannot be evaluated multiple times because the loop consumes the object that gets iterated. Be especially careful if you use such an object inside a Macro or an included template.

Velocity provides you with a loop counter reference which contains the number of the current iteration of the loop:

<table>
  #foreach( $customer in $customerList )
    <tr><td>$velocityCount</td><td>$customer.Name</td></tr>
  #end
</table>

The default name for the loop counter and its starting value is specified through runtime properties. By default it starts at 1 and is called $velocityCount.

# Default name of the loop counter
# variable reference.
directive.foreach.counter.name = velocityCount

# Default starting value of the loop
# counter variable reference.
directive.foreach.counter.initial.value = 1

The Velocity loop counter variable does support nested loops, so the following example would work as expected:

Example 5.6. Nested Loops and the loop counter

#foreach ($category in $allCategories)
  Category # $velocityCount is $category

  #foreach ($item in $allItems)
    Item # $velocitycount is $item
  #end

#end

The loop counter is local to its surrounding loop and any inner loops hides but not resets it. Please note that it is not possible to access the count variable of the outer loop directly inside the inner loop.[10]

To avoid deadlocks and denial of service attacks, it is possible to set a maximum allowed number of times that a loop may be executed. By default there is no limit, but it can be set to an arbitrary number in the Velocity configuration.

# The maximum allowed number of loops.
directive.foreach.maxloops = -1


[8] It is stil possible to write a template that contains endless loops. It is just harder.

[9] The list of objects can be changed and extended, please see the Developers guide on how to do this.

[10] You can assign its value to another variable before entering the inner loop using #set and access it through this variable inside the inner loop.