On this page
列出,否则,项目,sep,中断,continue
Page Contents
Synopsis
列出序列(或集合)的最简单形式是:
<#list sequence as item>
Part repeated for each item
</#list>
并列出哈希的键值对(自 2.3.25 开始):
<#list hash as key, value>
Part repeated for each key-value pair
</#list>
但是,这些只是通用形式的特殊情况,如下所示。请注意,为简单起见,我们仅显示序列表的通用形式。只需将“ as item
”替换为“ as key, value
”即可获得哈希列表的通用形式。
通用形式 1:
<#list sequence as item>
Part repeated for each item
<#else>
Part executed when there are 0 items
</#list>
Where:
else
部分是可选的,仅从 FreeMarker 2.3.23 开始才受支持。sequence
:表达式的计算结果是我们要迭代的项的序列或集合item
:loop variable的名称(不是表达式)标签之间的各个“部分”可以包含任意 FTL(包括嵌套的
list
-s)
通用形式 2(自 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>
位置:请参见上面表格 1 的“位置”部分(因此else
部分也是可选的)。
Description
Simplest form
假设users
包含['Joe', 'Kate', 'Fred']
序列:
<#list users as user>
<p>${user}
</#list>
<p>Joe
<p>Kate
<p>Fred
list
指令针对指定为其第一个参数的序列(或集合)中的每个值,执行list
起始标记和list
结束标记(从现在开始list
的主体)之间的代码。对于每次这样的迭代,循环变量(在此示例中为user
)将存储当前项目的值。
循环变量(user
)仅存在于list
主体内部。另外,从循环内调用的宏/函数将看不到它(就像它是局部变量一样)。
列表哈希非常相似,但是您需要在as
之后提供两个变量名;一个用于哈希键,另一个用于关联值。假设products
是{ "apple": 5, "banana": 10, "kiwi": 15 }
:
<#list products as name, price>
<p>${name}: ${price}
</#list>
<p>apple: 5
<p>banan: 10
<p>kiwi: 15
请注意,并非所有的哈希变量都可以列出,因为其中一些无法枚举其键。尽管可以列出代表 Java Map
对象的散列,但实际上是安全的。
else directive
Note:
从 FreeMarker 2.3.23 开始,仅支持list
内的else
如果有 0 个项目,则必须打印一些特殊的东西而不是什么都不打印,则使用else
指令:
<#list users as user>
<p>${user}
<#else>
<p>No users
</#list>
输出结果与前面的示例相同,除了users
包含 0 个项目时:
<p>No users
请注意,循环变量(user
)在else
标签和list
结束标签之间不存在,因为该部分不是循环的一部分。
else
必须字面意义上(在源代码中为平均值)位于list
指令的主体内。也就是说,您不能将其移到宏或包含的模板中。
items directive
Note:
自 FreeMarker 2.3.23 起存在items
如果必须在第一个列表项之前和最后一个列表项之后(至少有一个项目)打印(或执行)操作,则使用items
指令。一个典型的例子:
<#list users>
<ul>
<#items as user>
<li>${user}</li>
</#items>
</ul>
</#list>
<ul>
<li>Joe</li>
<li>Kate</li>
<li>Fred</li>
</ul>
如果有 0 个项目,则上面的项目将不会打印任何内容,因此最终不会出现空的<ul></ul>
。
也就是说,当list
指令没有as item
参数时,如果至少有一项,则其主体仅执行一次,否则则根本不执行。它是将为每个项目运行的强制嵌套items
指令的主体,因此,它也是items
指令,它使用as item
而不是list
定义了循环变量。
具有items
的list
指令也可以具有else
指令:
<#list users>
<ul>
<#items as user>
<li>${user}</li>
</#items>
</ul>
<#else>
<p>No users
</#list>
一些进一步的细节:
解析器将检查不带
as item
参数的list
始终具有嵌套的items
指令,并且items
指令始终具有不带as item
参数的封闭list
。这是在分析模板时检查的,而不是在执行模板时检查的。因此,这些规则适用于 FTL 源代码本身,因此您不能将items
移出宏或包含的模板。list
可以有多个items
指令,但只能运行其中一个(只要您不离开并重新 Importing 封闭的list
指令);并进一步尝试调用items
将导致错误。因此,例如,可以在不同的if
-else
分支上利用多个items
,但不能重复两次。items
指令不能具有自己的嵌套else
指令,只有封闭的list
可以具有循环变量(
user
)仅存在于items
指令的主体内。
sep directive
Note:
自 FreeMarker 2.3.23 起存在sep
sep
用于必须在每个项目之间显示某些内容(但不要在第一个项目之前或最后一个项目之后)时显示。例如:
<#list users as user>${user}<#sep>, </#list>
Joe, Kate, Fred
上面的<#sep>, </#list>
是<#sep>, </#sep></#list>
的简写;如果您将sep
结束标记放置在无论如何封闭封闭指令的位置,都可以省略。在下一个示例中,您不能使用这样的缩写(HTML 标记什么也不会关闭,因为它们只是为 FreeMarker 输出的原始文本):
<#list users as user>
<div>
${user}<#sep>, </#sep>
</div>
</#list>
sep
只是<#if item?has_next>...</#if>
的简写。因此,它可以在有list
或items
循环变量可用的任何地方使用,它可以多次出现,并且可以具有任意嵌套的内容。
解析器确保sep
仅用于有可见循环变量的地方。这发生在模板的实际执行之前。因此,您不能将sep
从关联的list
或items
指令内部移至宏或包含的模板中(解析器无法知道将从何处调用这些代码)。
break directive
Note:
break
在大多数情况下不建议使用,因为它不适用于<#sep>
和item?has_next
。相反,在列出序列之前,请使用sequence?take_while(predicate)剪切序列。另请参见示例here.
您可以随时使用break
指令退出迭代。例如:
<#list 1..10 as x>
${x}
<#if x == 3>
<#break>
</#if>
</#list>
1
2
3
可以将break
指令放置在list
内的任何位置,只要它具有as item
参数即可;否则,可以将其放置在items
指令内的任何位置。但是,强烈建议将其放在迭代中您执行的所有其他操作之前或之后。否则,很容易以输出中未封闭的元素结尾,否则会使模板更难以理解。特别是,避免从嵌套自定义指令(如<#list ...>...<@foo>...<#break>...</@foo>...</#list>
)的嵌套内容中突围,因为该指令的作者可能不希望关闭标记(</@foo>
)永远不会执行。
如果break
在items
内部,它将仅从items
退出,而不是从list
退出。通常,break
只会从为每个项目调用其主体的指令中退出,并且只能放置在该指令内部。因此,例如,不能在list
的else
部分中使用break
,除非list
嵌套在另一个break
-able 指令中。
将break
与sep
或?has_next
一起使用通常是个坏主意,因为它们不知道您是否会使用break
跳过其余项目。要解决这种情况,请参见these examples。
就像else
和items
一样,break
实际上必须位于指令的主体内才能进行扩展,并且不能移出宏或包含的模板。
continue directive
Note:
continue
在大多数情况下不建议使用,因为它不适用于<#sep>
,item?has_next
,item?counter
,item?index
,item?item_parity
等。相反,请使用sequence?filter(predicate)删除不需要的元素。另请参见示例here.
Note:
自 FreeMarker 2.3.27 起存在continue
指令。
您可以使用continue
指令跳过迭代主体的其余部分(直到</#list>
或</#items>
标记为止的部分),然后 FreeMarker 将 continue 进行下一项。例如:
<#list 1..5 as x>
<#if x == 3>
<#continue>
</#if>
${x}
</#list>
1
2
4
5
可以将continue
指令放置在list
内的任何位置,只要它具有as item
参数即可;否则,可以将其放置在items
指令内的任何位置。但是,强烈建议将其放在迭代中您执行的所有其他操作之前。否则,很容易以输出中未封闭的元素结尾,否则会使模板更难以理解。特别是,避免从嵌套自定义指令(如<#list ...>...<@foo>...<#continue>...</@foo>...</#list>
)的嵌套内容中突围,因为该指令的作者可能不希望关闭标记(</@foo>
)永远不会执行。
当您调用continue
时,将不会对该迭代执行sep
指令。总之,将continue
和sep
一起使用通常是个坏主意,如果您完全跳过项目,则?has_next
,?counter
,?index
,?item_parity
等也将无法正常工作。要解决这种情况,请参见these examples。
就像break
一样,continue
必须在指令的主体内部,其迭代需要“continue”,并且不能移出宏或包含的模板。
访问迭代状态
从 2.3.23 开始,内置循环变量是访问迭代当前状态的首选方式。例如,在这里我们使用内置的counter
和item_parity
循环变量(请参见在参考中):
<#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>
在 2.3.22 及更早版本中,有两个额外的循环变量代替检索迭代状态(为了向后兼容,它们仍然存在):
item_index
(*被item?index
弃用):循环中当前项目的索引(从 0 开始的数字)。item_has_next
(*被item?has_next
弃用):布尔值,它指示当前项目是否是序列中的最后一项。
因此在上面的示例中,您可以将${user?counter}
替换为${user_index + 1}
。
有条件地跳过项目
如果您需要跳过列表中的某些元素,通常不建议使用if directive,因为<#sep>
,item?has_next
,item?counter
,item?index
,item?item_parity
等将不可用,因为 FreeMarker 不知道哪些项目是并且将实际显示。相反,您应该尝试从要列出的序列中删除不需要的项目,然后将其列出(自 2.3.29 开始)。这是一些带有和不带有if
的典型示例。
Filtering
在此示例中,您要显示products
的推荐产品。这是if
的错误解决方案:
<#-- WRONG solution! The row parity classes will be possibly messed up: -->
<#list products as product>
<#if product.recommended>
<div class="${product?item_parity}Row">${product.name}</div>
</#if>
</#list>
这是使用filter built-in的好解决方案:
<#-- Good solution: -->
<#list products?filter(p -> p.recommended) as product>
<div class="${product?item_parity}Row">${product.name}</div>
</#list>
找到特定元素后停止列出
假设您有lines
行的列表,并且您需要在第一个空行处停止(如果有的话)。此外,您需要在元素之间<br>
。这是if
和break
的错误解决方案:
<#-- WRONG solution! <br> might be added after the last printed line: -->
<#list lines as line>
<#if line == ''>
<#break>
</#if>
${line}<#sep><br>
</#list>
这是使用take_while built-in的好解决方案(请注意,与if
break
解决方案相比,条件是相反的):
<#-- Good solution: -->
<#list lines?take_while(line -> line != '') as line>
${line}<#sep><br>
</#list>
嵌套循环相互转化
当然,list
或items
可以包含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
还可以使用冲突循环变量名称,例如:
<#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
处理缺失(空)元素
如您现在所知,list
指令将为列出值的每个元素重复其嵌套内容。但是,从技术上讲,元素列表中可能有孔(缺失值,Java null
-s)。嵌套的内容也将针对这些“孔”执行,但是循环变量(在as
关键字后指定名称的变量)将丢失。当 FreeMarker 在循环变量范围内发现没有给定名称的变量时,它将回落到更高的范围以进行查找。但是,这种后备行为在这种情况下可能会出现问题。考虑:
<#list xs as x>
${x!'Missing'}
</#list>
在此,作者的意图是为xs
的缺失元素(希望)打印“ Missing”。但是,如果不小心在较高范围内有一个x
变量(例如在数据模型中),则x
将对此值求值,以防当前列出的元素丢失,因此它将不会默认为“ Missing”。为了解决这个问题,程序员应该将fallback_on_null_loop_variable
配置设置设置为false
。 (不幸的是,为了向后兼容,默认值必须为true
.)在这种情况下,如果缺少循环变量,则不会发生任何后退。
Java 程序员注意事项
如果经典兼容模式list
也接受标量,并将其视为单元素序列。
如果您传递将java.util.Iterator
包装到list
的集合,则只能对其元素进行一次迭代,因为Iterator
本质上是一次性对象。当您尝试第二次列出这样的集合变量时,错误将中止模板处理。