list, else, items, sep, break

概要

形式 1:

<#list sequence as item>
    Part repeated for each item
<#else>
    Part executed when there are 0 items
</#list>

这里:

  • else 部分是可选的, 而且仅仅从 FreeMarker 2.3.23 版本开始支持。
  • sequence: 将我们想要迭代的项,算作是序列或集合的表达式
  • item: 循环变量 的名称 (不是表达式)
  • 在标签之间的多个 "parts" 可以是任意的FTL (包括嵌套的 list)

形式 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的 "这里" 部分相同。

描述

最简形式

假设 users 包含 ['Joe', 'Kate', 'Fred'] 序列:

<#list users as user>
  <p>${user}
</#list>
  <p>Joe
  <p>Kate
  <p>Fred

list 指令执行在 list 开始标签和 list 结束标签 ( list 中间的部分) 之间的代码, 对于在序列(或集合)中每个值指定为它的第一个参数。 对于每次迭代,循环变量(本例中的 user)将会存储当前项的值。

循环变量(user) 仅仅存在于 list 标签体内。 而且从循环中调用的宏/函数不会看到它(就像它只是局部变量一样)。

else 指令

Note:

在 list 中的 else 仅从 FreeMarker 2.3.23 版本开始支持。

当没有迭代项时,才使用 else 指令, 可以输出一些特殊的内容而不只是空在那里:

<#list users as user>
  <p>${user}
<#else>
  <p>No users
</#list>

该输出和之前示例是相同的,除了当 users 包含0项时:

  <p>No users

请注意,循环变量 (user) 在 else 标签和 list 结束标签中间不存在, 因为那部分不是循环中的部分。

else 必须是真的在 (也就是在源代码中) list 指令体内部。也就是说, 不能将它移出到宏或包含的模板中。

items 指令

Note:

items 从 FreeMarker 2.3.23 版本开始存在

如果不得不在第一列表项之前或在最后一个列表项之后打印一些东西, 那么就要使用 items 指令,但至少要有一项。典型的示例为:

<#list users>
  <ul>
    <#items as user>
      <li>${user}</li>
    </#items>
  </ul>
</#list>
  <ul>
      <li>Joe</li>
      <li>Kate</li>
      <li>Fred</li>
  </ul>

如果没有迭代项,那么上面的代码不会输出任何内容, 因此不用以空的 <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 指令通常会有一个包围的 list,它没有 as item 参数。当模板解析时就会检查,而不是当模板执行的时候。因此,这些规则也适用于FTL源代码本身, 所以不能将 items 移出到宏或者被包含的模板中。

  • list 可以有多个 items 指令, 但是只有一个允许执行(直到不离开或重新进入包围的 list 指令); 之后试图调用 items 会发生错误。所以多个 items 可以用于不同的 if-else 分支中去,但不能迭代两次。

  • items 指令不能有它自己的嵌入 else 指令,只能被包含的 list 可以有。

  • 循环变量 (user) 仅仅存在于 items 指令体内部。

sep 指令

Note:

sep 从 FreeMarker 2.3.23 版本开始存在。

当不得不显示介于每个迭代项(但不能在第一项之前或最后一项之后) 之间的一些内容时,可以使用 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 循环变量时,它就可以使用,并且不限次数。而且, 也可以有任意的 FTL 作嵌入的内容。

解析器会检查在 list ... as item 内部使用的 sep 或者 items 指令,所以不能将 sep 从重复的部分移出到宏或被包含的模板中。

break 指令

可以使用 break 指令在迭代的任意点退出。例如:

<#list 1..10 as x>
  ${x}
  <#if x == 3>
    <#break>
  </#if>
</#list>
  1
  2
  3

break 指令可以放在 list 中的任意位置,直到有 as item 参数, 否则,可以放在 items 指令中的任意位置。 如果 break 在 items 内部, 那么就只能从 items 开始时存在,而不能从 list 开始时存在。通常来说,break 将仅存在于为每个迭代项调用的指令体中,而且只能存在于这样的指令中。 例如不能在 list 的 else 部分使用 break,除非 list 内嵌到了其它 可以 break 的指令中。

像 else 和 items, break 只能在指令体内部使用,而不能移出到宏或被包含的模板中。

访问迭代状态

从 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}。

相互嵌套循环

很自然地,list 或 items 可以包含更多 list:

<#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

Java程序员请注意

如果经典兼容模式下 list 接受标量,并将它视为单元素序列。

如果传递包装了 java.util.Iterator 的集合到 list 中,那么只能迭代其中的元素一次,因为 Iterator 是它们天然的一次性对象。 当视图再次去迭代这样的集合变量时,会发生错误并中止模板处理。