Synopsis
Form 1:
<#list sequence as item>
Part repeated for each item
<#else>
Part executed when there are 0 items
</#list>
Where:
-
The
else
part is optional, and is only supported since FreeMarker 2.3.23. -
sequence
: Expressions evaluates to a sequence or collection of the items we want to iterate through -
item
: Name of the loop variable (not an expression) -
The various "parts" between the tags can
contain arbitrary FTL (including nested
list
-s)
Form 2 (since FreeMarker 2.3.23):
<#list sequence>
Part executed once if we have more than 0 items
<#items as item>
Part repeated for each item
</#items>
Part executed once if we have more than 0 items
<#else>
Part executed when there are 0 items
</#list>
Where: Same as the "Where" section of Form 1 above.
Description
Simplest form
Assuming users
contains the
['Joe', 'Kate', 'Fred']
sequence:
<#list users as user> <p>${user} </#list>
<p>Joe <p>Kate <p>Fred
The list
directive executes the code
between the list
start-tag and
list
end-tag (the body of
list
from now on) for each value in the
sequence (or collection) specified as its first parameter. For
each such iteration the loop variable (user
in
this example) will store the value of the current item.
The loop variable (user
) only exists
inside the list
body. Also, macros/functions
called from within the loop won't see it (as if it were a local
variable).
else directive
else
inside list
is
only supported since FreeMarker 2.3.23
The else
directive is used if when there
are 0 items, you have to print something special instead of just
printing nothing:
<#list users as user> <p>${user} <#else> <p>No users </#list>
This outputs the same as the earlier example, except when
users
contains 0 items:
<p>No users
Note that the loop variable (user
)
doesn't exist between the else
tag and the
list
end-tag, since that part is not part of
the loop.
else
must be literally (means, in the
source code) inside the body of the list
directive. That is, you can't moved it out into a macro or
included template.
items directive
items
exists since FreeMarker
2.3.23
The items
directive is used if you have
to print (or do) something before the first list item, and after
the last list item, as far as there's at least 1 item. A typical
example:
<#list users> <ul> <#items as user> <li>${user}</li> </#items> </ul> </#list>
<ul> <li>Joe</li> <li>Kate</li> <li>Fred</li> </ul>
If there are 0 items, the above won't print anything, thus
you don't end up with an empty
<ul></ul>
.
That is, when the list
directive has no
as item
parameter,
the body of its is executed exactly once if there's at least one
item, or not at all otherwise. It's the body of the mandatory
nested items
directive that will be run for
each item, and hence it's also the items
directive that defines the loop variable with as
item
, not
list
.
A list
directive with
items
also can have an else
directive:
<#list users> <ul> <#items as user> <li>${user}</li> </#items> </ul> <#else> <p>No users </#list>
Some further details:
-
The parser will check that a
list
withoutas item
parameter always has a nesteditems
directive, and that anitems
directive always has an enclosinglist
which has noas item
parameter. This is checked when the template is parsed, not when the template is executed. Thus, these rules apply on the FTL source code itself, so you can't moveitems
out into a macro or included template. -
A
list
can have multipleitems
directives, but only one of them will be allowed to run (as far as you don't leave and re-enter the enclosinglist
directive); and further attempts to callitems
will cause error. So multipleitems
can be utilized on differentif
-else
branches for example, but not for iterating twice. -
items
directive can't have its own nestedelse
directive, only the enclosinglist
can have -
The loop variable (
user
) only exists inside the body of theitems
directive.
sep directive
sep
exists since FreeMarker
2.3.23
sep
is used when you have to display
something between each item (but not before the first item or
after the last item). For example:
<#list users as user>${user}<#sep>, </#list>
Joe, Kate, Fred
Above, <#sep>, </#list>
is a
shorthand for <#sep>,
</#sep></#list>
; the sep
end-tag can be omitted if you would put it where the enclosing
directive is closed anyway. In the next example, you couldn't use
such abbreviation (HTML tags close nothing, as they are just raw
text to output for FreeMarker):
<#list users as user> <div> ${user}<#sep>, </#sep> </div> </#list>
As sep
is just a convenient way of
writing <#if
item?has_next>...</#if>
,
it can be used anywhere where there's a list
or
items
loop variable is available, and for
unlimited times. Also, it can have arbitrary FTL as nested
content.
The parser will check that sep
is used
inside list ... as
item
or an items
directive, so you
can't move sep
out from the repeated part into
a macro or included template.
break directive
You can exit the iteration at any point with the
break
directive. For example:
<#list 1..10 as x> ${x} <#if x == 3> <#break> </#if> </#list>
1 2 3
The break
directives can be placed
anywhere inside list
as far as it has
as item
parameter,
otherwise it can be placed anywhere inside the
items
directive. If the
break
is inside items
, it
will only exit from items
, not from
list
. In general, break
will
only exit from the directive whose body is called for each item,
and can only be placed inside such directive. So for example can't
use break
inside list
's
else
section, unless there's the
list
is nested into another
break
-able directive.
Just like else
and
items
, break
must be
literally inside body of the directive to break out from, and
can't be moved out into a macro or included template.
Accessing iteration state
Starting from 2.3.23, loop variable built-ins is
the preferred way of accessing current state of the iteration. For
example, here we use the counter
and
item_parity
loop variable built-ins (see all of
them in the
Reference):
<#list users> <table> <#items as user> <tr class="${user?item_parity}Row"> <td>${user?counter} <td>${user} </#items> </table> </#list>
<table> <tr class="oddRow"> <td>1 <td>Joe <tr class="evenRow"> <td>2 <td>Kate <tr class="oddRow"> <td>3 <td>Fred </table>
In 2.3.22 and earlier, there were two extra loop variables to retrieve the iteration state instead (and they still exist for backward compatibility):
-
item_index
(deprecated byitem?index
): The index (0-based number) of the current item in the loop. -
item_has_next
(deprecated byitem?has_next
): Boolean value that tells if the current item is the last in the sequence or not.
so in the above example, you could replace
${user?counter}
with ${user_index +
1}
.
Nesting loops into each other
Naturally, list
or
items
can contain further
list
-s:
<#list 1..2 as i> <#list 1..3 as j> i = ${i}, j = ${j} </#list> </#list>
i = 1, j = 1 i = 1, j = 2 i = 1, j = 3 i = 2, j = 1 i = 2, j = 2 i = 2, j = 3
It's also allowed to use clashing loop variable names like:
<#list 1..2 as i> Outer: ${i} <#list 10..12 as i> Inner: ${i} </#list> Outer again: ${i} </#list>
Outer: 1 Inner: 10 Inner: 11 Inner: 12 Outer again: 1 Outer: 2 Inner: 10 Inner: 11 Inner: 12 Outer again: 2
Notes for Java programmers
If classic compatible mode
list
accepts a scalar too and treats it as a
single-element sequence.
If you pass an collection that
wraps an java.util.Iterator
to the
list
, you can iterate over its elements only
once, since Iterator
s are by their nature
one-off objects. When you try to list a such collection variable
for the second time, an error will abort template
processing.