escape, noescape

概要

<#escape identifier as expression>
  ...
  <#noescape>...</#noescape>
  ...
</#escape>

描述

当你使用escape指令包围模板中的一部分时,在块中出现的插值 (${...}) 会和转义表达式自动结合。这是一个避免编写相似表达式的很方便的方法。 它不会影响在字符串形式的插值(比如在 <#assign x = "Hello ${user}!">)。而且,它也不会影响数值插值 (#{...})。

例如:

<#escape x as x?html>
  First name: ${firstName}
  Last name: ${lastName}
  Maiden name: ${maidenName}
</#escape>

事实上它等同于:

  First name: ${firstName?html}
  Last name: ${lastName?html}
  Maiden name: ${maidenName?html}

请注意,它和你在指令中用什么样的标识符无关 - 它仅仅是作为一个转义表达式的正式参数。

当在调用宏或者 include 指令时, 理解 在模板文本 中转义仅仅对出现在 <#escape ...></#escape> 中的插值起作用是很重要的。 也就是说,它不会转义文本中 <#escape ...> 之前的东西或 </#escape> 之后的东西, 也不会从 escape 的部分中来调用。

<#assign x = "<test>">
<#macro m1>
  m1: ${x}
</#macro>
<#escape x as x?html>
  <#macro m2>m2: ${x}</#macro>
  ${x}
  <@m1/>
</#escape>
${x}
<@m2/>

将会输出:

  &lt;test&gt;
  m1: <test>
<test>
m2: &lt;test&gt;

从更深的技术上说, escape 指令的作用是用在模板解析的时间而不是模板处理的时间。 这就表示如果你调用一个宏或从一个转义块中包含另外一个模板, 它不会影响在宏/被包含模板中的插值,因为宏调用和模板包含被算在模板处理时间。 另外一方面,如果你用一个转义区块包围一个或多个宏声明(算在模板解析时间,和宏调用想法), 那么那些宏中的插值将会和转义表达式合并。

有时需要暂时为一个或两个在转义区块中的插值关闭转义。你可以通过关闭, 过后再重新开启转义区块来达到这个功能,但是那么你不得不编写两遍转义表达式。 你可以使用非转义指令来替代:

<#escape x as x?html>
  From: ${mailMessage.From}
  Subject: ${mailMessage.Subject}
  <#noescape>Message: ${mailMessage.htmlFormattedBody}</#noescape>
  ...
</#escape>

和这个是等同的:

  From: ${mailMessage.From?html}
  Subject: ${mailMessage.Subject?html}
  Message: ${mailMessage.htmlFormattedBody}
  ...

转义可以被嵌套(尽管你不会在罕见的情况下来做)。 因此,你可以编写如下面代码(这个例子固然是有点伸展的, 正如你可能会使用 list 来迭代序列中的每一项, 但是我们现在所做的是阐述这个观点)的东西:

<#escape x as x?html>
  Customer Name: ${customerName}
  Items to ship:
  <#escape x as itemCodeToNameMap[x]>
    ${itemCode1}
    ${itemCode2}
    ${itemCode3}
    ${itemCode4}
  </#escape>
</#escape>

实际上和下面是等同的:

  Customer Name: ${customerName?html}
  Items to ship:
    ${itemCodeToNameMap[itemCode1]?html}
    ${itemCodeToNameMap[itemCode2]?html}
    ${itemCodeToNameMap[itemCode3]?html}
    ${itemCodeToNameMap[itemCode4]?html}

当你在嵌入的转义区块内使用非转义指令时,它仅仅不处理一个单独层级的转义。 因此,为了在两级深的转义区块内完全关闭转义,你需要使用两个嵌套的非转义指令。