freeMarker模板语言参考:指令参考

指令参考

Alphabetical index(按字母顺序搜索)

中文官方参考手册

一.assign

写法:

<#assign name1=value1 name2=value2 ... nameN=valueN>
或
<#assign same as above... in namespacehash>
或
<#assign name>
  capture this
</#assign>
或
<#assign name in namespacehash>
  capture this
</#assign>
  • name:变量的名字。 它不是表达式。而它可以写作是字符串,如果变量名包含保留字符这是很有用的, 比如 <#assign “foo-bar” = 1>。 请注意这个字符串没有展开插值(如"${foo}"); 如果需要赋值一个动态创建的名字,那么不得不使用 这个技巧。
  • =:赋值操作符。 它也可以是一个简写的赋值操作符(从 FreeMarker 2.3.23 版本开始): ++,–, +=,-=, *=,/= 或 %=。比如 <#assign x++> 和 <#assign x = x + 1> 是一样的,并且 <#assign x += 2> 和 <#assign x = x + 2> 是相同的。 请注意, ++ 通常意味着算术加法 (对于非数字将会失败),不像 + 或 += 可以进行字符连接等重载操作。
  • value: 存储的值。是表达式。
  • namespacehash:(通过 import) 为命名空间创建的哈希表。是表达式

描述

使用该指令你可以创建一个新的变量, 或者替换一个已经存在的变量。注意仅仅顶级变量可以被创建/替换 (也就是说你不能创建/替换 some_hash.subvar, 除了 some_hash)。

应用

1.变量 seq 存储一个序列

<#assign seq = ["foo", "bar", "baz"]>

2.变量 x 中存储增长的数字

<#assign x++>
或者是
<#assign x=x+1>

3.可以使用一个 assign 标记来进行多次定义。比如这个会做上面两个例子中相同的事情:

<#assign
  seq = ["foo", "bar", "baz"]
  x++
>

4.assign 指令在命名空间中创建变量(在assign之前引入import一个文件,然后在assign中调用import文件)
通常它在当前的命名空间 (也就是和标签所在模板关联的命名空间)中创建变量。
但如果你是用了 in namespacehash, 那么你可以用另外一个 命名空间 来创建/替换变量。
比如,这里你在命名空间中 /mylib.ftl 创建/替换了变量 bgColor:

<#import "/mylib.ftl" as my>
<#assign bgColor="red" in my>

5.assign 的极端使用是当它捕捉它的开始标记和结束标记中间生成的输出时。
也就是说,在标记之间打印的东西将不会在页面上显示, 但是会存储在变量中

<#macro myMacro>foo</#macro>
<#assign x>
  <#list 1..3 as n>
    ${n} <@myMacro />
  </#list>
</#assign>
Number of words: ${x?word_list?size}
${x}

在这里插入图片描述
请注意,你不应该使用它来往字符串中插入变量

错误写法:
<#assign x>Hello ${user}!</#assign> <#-- BAD PRACTICE! -->
正确写法:
<#assign x="Hello ${user}!">
 	  <#assign x>
      <#list ["星期一", "星期二", "星期三", "星期四", "星期五", 
      "星期六", "星期天"]as n>
      ${n}
      </#list>
      </#assign>
      ${x}

在这里插入图片描述


二.attempt, recover

1.写法:

<#attempt>
  attempt block
<#recover>
  recover block
</#attempt>
  • attempt block:任意内容的模板块。这是会被执行的, 但是如果期间发生了错误,那么这块内容的输出将会回滚, 之后 recover block 就会被执行。
  • recover block: 任意内容的模板块。 这个仅在 attempt block 执行期间发生错误时被执行。你可以在这里打印错误信息或其他操作
  • recover 是强制的。 attempt/recover 可以嵌套在其他 attempt block 或 recover block中

描述

如果你想让页面成功输出内容,尽管它在页面特定位置发生错误也这样, 那么这些指令就是有用的。
如果一个错误在 attempt block 执行期间发生, 那么模板执行就会中止,但是 recover block 会代替 attempt block 执行。
如果在 attempt block 执行期间没有发生错误, 那么 recover block 就会忽略
类似try-catch 抛出异常

Primary content
<#attempt>
  Optional content: ${thisMayFails}
<#recover>
  Ops! The optional content is not available.
</#attempt>
Primary content continued

如果 thisMayFails 变量不存在,将会输出:
在这里插入图片描述
如果 thisMayFails 变量存在而且值为 123,将会输出:
在这里插入图片描述

三.compress

写法:

<#compress>
  ...
</#compress>

描述

当你使用了对空白不敏感的格式(比如HTML或XML) 时压缩指令对于移除多余的 空白 是很有用的。
它捕捉在指令体(也就是在开始标签和结束标签中)中生成的内容, 然后缩小所有不间断的空白序列到一个单独的空白字符。
如果被替代的序列包含换行符或是一段空间,那么被插入的字符也会是一个 换行符。 开头和结尾的不间断的空白序列将会完全被移除

  <#assign x = "    moo  \n\n   ">
    (<#compress>
    1 2  3   4    5
    ${moo}
    test only

    I said, test only

    </#compress>)

在这里插入图片描述

<#compress>...</#compress>:消除空白行。 

<@compress single_line=true>...</@compress>
将输出压缩为一行。都需要包裹所需文档
<#t> // 去掉左右空白和回车换行 

<#lt>// 去掉左边空白和回车换行 

<#rt>// 去掉右边空白和回车换行 

<#nt>// 取消上面的效果

四.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/>

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

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

五.flush

写法:

<#flush>

描述
当 FreeMarker 生成输出时,它通常不会立即发送到最终接收端 (比如web浏览器或最终的文件),而是会将内容累积在缓冲区,发送一个大块的内容。
缓冲区的精确规则不是由 FreeMarker 决定的,而是由嵌入的软件决定的。 将缓冲区中累积的内容发送出去称为冲洗。
尽管冲洗是自动发生的, 有时你想在模板处理时的一点强制执行,这就是 flush 指令要做的。
如果需要在确定之处用到它,这是由程序员决定的,而不是设计师
请注意, flush 告诉嵌入的软件我们想要冲洗, 那么也许就会决定忽略该请求。这不由 FreeMarker 之手控制
冲洗简单调用当前使用 java.io.Writer 实例的 flush() 方法。 整个缓冲区和冲洗机制由 Writer(就是传递给 Template.process 方法的参数)实现; FreeMarker不用来处理它


## 六.ftl 写法:
<#ftl param1=value1 param2=value2 ... paramN=valueN>
  • param1, param2 等: 参数的名字,不是表达式。允许的参数有: encoding, strip_whitespace, strip_text 等
  • value1, value2 等: 参数的值。必须是一个常量表达式(如 true, 或 “ISO-8859-5”,或 {x:1, y:2})。它不能用变量

描述

告诉 FreeMarker 和其他工具关于模板的信息, 而且帮助程序员来自动检测一个文本文件是否是 FTL 文件。
该指令, 如果存在,必须是模板的第一句代码。该指令前的任何 空白 将被忽略。 该指令的老式语法(#-less)格式是不支持

参数:

  • encoding: 使用它,你可以在模板文件本身中为模板指定编码方式(字符集)
  • strip_whitespace: 这将开启/关闭 空白剥离。 合法的值是布尔值常量 true 和 false (为了向下兼容,字符串 “yes”,“no”, “true”,“false” 也是可以的)。 默认值(也就是当你不使用这个参数时)是依赖于程序员对 FreeMarker 的配置, 但是对新的项目还应该是 true
  • strip_text:当开启它时, 当模板被解析时模板中所有顶级文本被移除。这个不会影响宏,指令,或插值中的文本。 合法值是布尔值常量 true 和 false
  • strict_syntax:这会开启/关闭"严格的语法"。 合法值是布尔值常量 true 和 false (为了向下兼容,字符串 “yes”,“no”, “true”,“false” 也是可以的)。 默认值(也就是当你不使用这个参数时)是依赖于程序员对 FreeMarker 的配置, 但是对新的项目还应该是 true。
  • ns_prefixes:这是关联结点命名空间前缀的哈希表。
  • attributes:这是关联模板任意属性(名-值对)的哈希表。 属性的值可以是任意类型(字符串,数字,序列等)。

该指令也决定模板是否使用尖括号语法(比如 <#include ‘foo.ftl’>)或 方括号语法 (如 [#include ‘foo.ftl’])。简单而言, 该指令使用的语法将会是整个模板使用的语法, 而不管 FreeMarker 是如何配置的。


七.function, return

写法:

<#function name param1 param2 ... paramN>
  ...
  <#return returnValue>
  ...
</#function>
  • name:方法变量的名称(不是表达式)
  • param1, param2 等: 局部变量的名称, 存储参数的值(不是表达式),在 = 号后面和默认值 (是表达式)是可选的。
  • paramN,最后一个参数, 可以可选的包含一个尾部省略(…), 这就意味着宏接受可变的参数数量。局部变量 paramN 将是额外参数的序列。
  • returnValue: 计算方法调用值的表达式。
  • return 指令可以在 <#function …> 和 </#function> 之间被用在任意位置和任意次数。
  • 没有默认值的参数必须在有默认值参数 (paramName=defaultValue) 之前

描述

创建一个方法变量(在当前命名空间中,如果你知道命名空间的特性)。
这个指令和 macro 指令 的工作方式一样,除了 return 指令必须有一个参数来指定方法的返回值,而且视图写入输出的将会被忽略。
如果到达 </#function> (也就是说没有 return returnValue), 那么方法的返回值就是未定义变量

应用

1.创建一个方法来计算两个数的平均值

官方文档:
<#function avg x y>
  <#return (x + y) / 2>
</#function>
${avg(10, 20)}

2.创建一个方法来计算多个数的平均值

平均值方法
<#function avg nums...>
  <#local sum = 0>
  <#list nums as num>
    <#local sum = sum + num>
  </#list>
  <#if nums?size != 0>
    <#return sum / nums?size>
  </#if>
</#function>
${avg(10, 20)}
${avg(10, 20, 30, 40)}
${avg()!"N/A"}

在这里插入图片描述


## 八.global 1.写法:
<#global name=value>
或
<#global name1=value1 name2=value2 ... nameN=valueN>
或
<#global name>
  capture this
</#global>
  • name:变量的名称。 它不是表达式。但它可以被写作是字符串形式,如果变量名包含保留字符这是很有用的, 比如 <#global “foo-bar” = 1>。 注意这个字符串没有扩展插值(如 “${foo}”)。
  • =:赋值操作符,也可以简写的赋值操作符之一 (++, +=,等…),和 assign 指令 相似
  • value:存储的值,是表达式。

描述

该指令和 assign 相似, 但是被创建的变量在所有的 命名空间 中都可见, 但又不会存在于任何一个命名空间之中。精确地说,正如你会创建 (或替换)一个数据模型变量。因此,这个变量是全局的。如果在数据模型中, 一个相同名称的变量存在的话,它会被使用该指令创建的变量隐藏。 如果在当前的命名空间中,一个相同名称的变量存在的话, 那么会隐藏由 global 指令创建的变量。

例如,用 <#global x = 1> 创建一个变量, 那么在所有命名空间中 x 都可见, 除非另外一个称为 x 的变量隐藏了它 (比如你已经用 <#assign x = 2> 创建了一个变量)。 这种情形下,你可以使用 特殊变量 globals,比如 ${.globals.x}。请注意, 使用 globals 你看到所有全局可访问的变量; 不但由 global 指令创建的变量,而且是数据模型中的变量。

自定义JSP标记的用户请注意:用这个指令创建的变量集合和JSP页面范围对应。 这就意味着,如果自定义JSP标记想获得一个页面范围的属性(page-scope bean), 在当前命名空间中一个相同名称的变量,从JSP标记的观点出发,将不会隐藏。
利用这个语法给变量赋值,那么这个变量在所有的namespace中是可见的,如果这个变量被当前的assign语法覆盖如<#global x=2><#assign x=1>在当前页面里x=2将被隐藏,或者通过${.globals.x} 来访问


九.if, else, elseif

1.写法:

<#if condition>
  ...
<#elseif condition2>
  ...
<#elseif condition3>
  ...
...
<#else>
  ...
</#if>
  • condition, condition2, 等:将被计算成布尔值的表达式
  • elseif 和 else 是可选的

描述

你可以使用 if, elseif 和 else 指令来条件判断是否越过模板的一个部分。
condition 必须计算成布尔值, 否则错误将会中止模板处理。
elseif 和 else 必须出现在 if 内部 (也就是,在 if 的开始标签和结束标签之间)。
if 中可以包含任意数量的 elseif(包括0个) 而且结束时 else 是可选的。

应用

1.只有 if 没有 elseif 和 else:

<#if x == 1>
  x is 1
</#if>

2.只有 if 没有 elseif 但是有 else

<#if x == 1>
  x is 1
<#else>
  x is not 1
</#if>

3.有 if 和两个 elseif 但是没有 else

<#if x == 1>
  x is 1
<#elseif x == 2>
  x is 2
<#elseif x == 3>
  x is 3
</#if>

4.有 if 和三个 elseif 还有 else:

<#if x == 1>
  x is 1
<#elseif x == 2>
  x is 2
<#elseif x == 3>
  x is 3
<#elseif x == 4>
  x is 4
<#else>
  x is not 1 nor 2 nor 3 nor 4
</#if>

5.嵌套 if 指令

<#if x == 1>
  x is 1
  <#if y == 1>
    and y is 1 too
  <#else>
    but y is not
  </#if>
<#else>
  x is not 1
  <#if y < 0>
    and y is less than 0
  </#if>
</#if>

十.import

写法:

<#import path as hash>
  • path:模板的路径。 这是一个算作是字符串的表达式。(换句话说,它不是一个固定的字符串, 它可以是这样的一些东西,比如,profile.baseDir + “/menu.ftl”。)
  • hash: 访问命名空间的哈希表变量不带引号的名字。不是表达式。 (如果要引入动态创建的名字,那么就不得不使用 这个技巧。)

描述

引入一个库。也就是说,它创建一个新的空命名空间, 然后在那个命名空间中执行给定 path 参数中的模板, 所以模板用变量(宏,函数等)填充命名空间。 然后使得新创建的命名空间对哈希表的调用者可用。 这个哈希表变量将会在命名空间中,由 import (就像你可以用 assign 指令来创建一样。) 的调用者被创建成一个普通变量,名字就是 hash 参数给定的。

如果你用同一个 path 多次调用 import,它会创建命名空间, 但是只运行第一次 import 的调用。 后面的调用仅仅创建一个哈希表变量,你只是通过它来访问 同一个 命名空间。

由引入的模板打印的输出内容将会被忽略 (不会在包含它的地方被插入)。模板的执行是来用变量填充命名空间, 而不是写到输出中

<#import "/libs/mylib.ftl" as my>

<@my.copyright date="1999-2002"/>

path 参数可以是一个相对路径,比如 “foo.ftl” 和 “…/foo.ftl”,或者是像 “/foo.ftl” 一样的绝对路径。 相对路径是相对于使用 import 指令模板的目录。 绝对路径是程序员配置 FreeMarker 时定义的相对于根路径 (通常指代"模板的根目录")的路径。

通常使用/(斜杠)来分隔路径组成, 而不是(反斜杠)。如果你从你本地的文件系统中加载模板, 那么它使用反斜杠(比如在Windows环境下),FreeMarker 将会自动转换它们。

像 include 指令一样,获得机制 和 本地化查找 也可以用来解决路径问题


十一.include

1.写法:

<#include path>
或
<#include path options>
  • path: 要包含文件的路径;一个算作是字符串的表达式。(用其他话说, 它不用是一个固定的字符串,它也可以是像 profile.baseDir + "/menu.ftl"这样的东西。)
  • options: 一个或多个这样的选项: encoding=encoding, parse=parse

options:

  1. encoding: 算作是字符串的表达式
  2. parse: 算作是布尔值的表达式(为了向下兼容,也接受一部分字符串值)
  3. ignore_missing: 算作是布尔值的表达式

描述

可以使用它在你的模板中插入另外一个 FreeMarker 模板文件 (由 path 参数指定)。 被包含模板的输出格式是在 include 标签出现的位置插入的。 被包含的文件和包含它的模板共享变量,就像是被复制粘贴进去的一样。 include 指令不能由被包含文件的内容所替代, 它只是当 FreeMarker 每次在模板处理期间到达 include 指令时处理被包含的文件。所以对于如果 include 在 list 循环之中的例子, 你可以为每个循环周期内指定不同的文件名

应用

假设 /common/copyright.ftl 包含:

Copyright 2001-2002 ${me}<br>
All rights reserved.
<#assign me = "Juila Smith">
<h1>Some test</h1>
<p>Yeah.
<hr>
<#include "/common/copyright.ftl">

在这里插入图片描述

十二.list, else, items, sep, break

1.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)

1.2写法二:

<#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’] 序列

	<#assign users= ['Joe', 'Kate', 'Fred']>
    <#list users as user>
        <p>${user}
    </#list>

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

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

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

<#assign users=[]>
    <#list users as user>
    <p>${user}
    <#else>
    <p>No users
    </#list>

该输出和之前示例是相同的,除了当 users 包含0项时:
在这里插入图片描述
请注意,循环变量 (user) 在 else 标签和 list 结束标签中间不存在, 因为那部分不是循环中的部分。

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

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

	<#assign users= ['Joe', 'Kate', 'Fred']>
    <#list users>
    <ul>
        <#items as user>
        <li>${user}</li>
        </#items>
    </ul>
    </#list>

在这里插入图片描述
也就是说,当 list 指令没有 as item 参数, 如果只有一个迭代项,指令体中的代码仅仅执行一次,否则就不执行。
必须内嵌的 items 指令体会对每个迭代项执行, 那么 items 指令使用 as item 定义循环变量,而不是 list

有 items 的 list 指令也可以有 else 指令:

<#assign users= ['Joe', 'Kate', 'Fred']>
<#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 指令
当不得不显示介于每个迭代项(但不能在第一项之前或最后一项之后) 之间的一些内容时,可以使用 sep。

<#assign users= ['Joe', 'Kate', 'Fred']>
<#list users as user>${user}<#sep>, </#list>

在这里插入图片描述
上面的 <#sep>, </#list> 是 <#sep>, </#sep></#list> 的简写; 如果将它放到被包含的指令关闭的位置时,sep 结束标签可以忽略。下面的示例中,就不能使用该简写 (HTML标签不会结束任何代码,它们只是 FreeMarker 输出的原生文本):

<#assign users= ['Joe', 'Kate', 'Fred']>
<#list users as user>
  <div>
    ${user}<#sep>, </#sep>
  </div>
</#list>

sep 是编写 <#if item?has_next>…</#if> 的方便形式,有 list 或 items 循环变量时,它就可以使用,并且不限次数。而且, 也可以有任意的 FTL 作嵌入的内容。

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

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

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

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

访问迭代状态
循环变量内建函数 就是访问当前迭代状态的最佳方式
使用 counter 和 item_parity 循环变量内建函数(在 循环变量内建函数参考 中查看它们全部)

 	<#assign users= ['Joe', 'Kate', 'Fred']>
    <#list users>
    <table border=4 cellspacing=0 cellpadding=4>
        <#items as user>
        <tr class="${user?item_parity}Row">
            <td>${user?counter}</td>
            <td>${user_index + 1}</td>
            <td>${user}</td>
        </#items>
    </table>
    </#list>
    所以在上面的示例中,可以将 ${user?counter} 替换为 ${user_index + 1}

在这里插入图片描述
相互嵌套循环
list 或 items 可以包含更多 list:

	<#list 1..2 as i>
        <#list 2..5 as j>
            i: ${i},j:${j} 
            (${i},${j})<br>
        </#list>
    </#list>

在这里插入图片描述

	<#list 1..2 as i>
        Outer: ${i}
        <#list 10..12 as i>
            Inner: ${i}
        </#list>
        Outer again: ${i}<br>
    </#list>

在这里插入图片描述


## 十三.local 写法:
<#local name=value>
或
<#local name1=value1 name2=value2 ... nameN=valueN>
或
<#local name>
  capture this
</#local>
  • name: 在root中局部对象的名称。它不是一个表达式。但它可以被写作是字符串形式, 如果变量名包含保留字符,这是很有用的,比如 <#local “foo-bar” = 1>。 请注意,这个字符串没有扩展插值(如"${foo}")。
  • =:赋值操作符,也可以简写的赋值操作符之一 (++,+= 等…),和 the assign 指令 相似。
  • value: 存储的值,是表达式。

描述

它和 assign 指令 类似,但是它创建或替换局部变量。 这仅仅在宏和方法的内部定义才会有作用。


十四.macro, nested, return

写法:

<#macro name param1 param2 ... paramN>
  ...
  <#nested loopvar1, loopvar2, ..., loopvarN>
  ...
  <#return>
  ...
</#macro>
  • name: 宏变量的名称,它不是表达式。和 顶层变量 的语法相同,比如 myMacro 或 my-macro。 然而,它可以被写成字符串的形式,如果宏名称中包含保留字符时,这是很有用的, 比如 <#macro “foo~bar”>…。 注意这个字符串没有扩展插值(如 “${foo}”)。
  • param1, param2,等…: 局部变量 的名称,存储参数的值 (不是表达式),在 = 号后面和默认值(是表达式)是可选的。 默认值也可以是另外一个参数,比如 <#macro section title label=title>。参数名称和 顶层变量 的语法相同,所以有相同的特性和限制。
  • paramN, 最后一个参数,可能会有三个点(…), 这就意味着宏接受可变数量的参数,不匹配其它参数的参数可以作为最后一个参数 (也被称作笼统参数)。当宏被命名参数调用, paramN 将会是包含宏的所有未声明的键/值对的哈希表。当宏被位置参数调用, paramN 将是额外参数的序列。 (在宏内部,要查找参数,可以使用 myCatchAllParam?is_sequence。)
  • loopvar1, loopvar2等…: 可选的,循环变量 的值, 是 nested 指令想为嵌套内容创建的。这些都是表达式
  • return 和 nested 指令是可选的,而且可以在 <#macro …> 和 </#macro> 之间被用在任意位置和任意次数。
  • 没有默认值的参数必须在有默认值参数 (paramName=defaultValue) 之前。

描述

创建一个宏变量(在当前命名空间中,如果你知道命名空间的特性)
宏变量存储模板片段(称为宏定义体)可以被用作 自定义指令。 这个变量也存储自定义指令的被允许的参数名。当你将这个变量作为指令时, 你必须给所有参数赋值,除了有默认值的参数。 默认值当且仅当你调用宏而不给参数赋值时起作用。

应用

变量会在模板开始时被创建;而不管 macro 指令放置在模板的什么位置。因此,这样也可以:

<#-- call the macro; the macro variable is already created: -->
<@test/>
...

<#-- create the macro variable: -->
<#macro test>
  Test text
</#macro>

然而,如果宏定义被插在 include 指令中, 它们直到 FreeMarker 执行 include 指令时才会可用。

例如:没有参数的宏

<#macro test>
  Test text
</#macro>
<#-- call the macro: -->
<@test/>

在这里插入图片描述
示例:有参数的宏

<#macro test foo bar baaz>
  Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<#-- call the macro: -->
<@test foo="a" bar="b" baaz=5*5-2/>

在这里插入图片描述
示例:有参数和默认值参数的宏

<#macro test foo bar="Bar" baaz=-1>
  Test text, and the params: ${foo}, ${bar}, ${baaz}
</#macro>
<@test foo="a" bar="b" baaz=5*5-2/>
<@test foo="a" bar="b"/>
<@test foo="a" baaz=5*5-2/>
<@test foo="a"/>

在这里插入图片描述
示例:更为复杂的宏

<#macro list title items>
  <p>${title?cap_first}:
  <ul>
    <#list items as x>
      <li>${x?cap_first}
    </#list>
  </ul>
</#macro>
<@list items=["mouse", "elephant", "python"] title="Animals"/>

在这里插入图片描述
示例:支持多个参数和命名参数的宏

<#macro img src extra...>
  <img src="/context${src?html}" 
  <#list extra?keys as attr>
    ${attr}="${extra[attr]?html}"
  </#list>
  >
</#macro>
<@img src="/images/test.png" width=100 height=50 alt="Test"/>

在这里插入图片描述
示例:支持多个位置参数的宏,不管是否使用命名或位置参数传递

<#macro m a b ext...>
  a = ${a}
  b = ${b}
  <#if ext?is_sequence>
    <#list ext as e>
      ${e?index} = ${e}
    </#list>
  <#else>
    <#list ext?keys as k>
      ${k} = ${ext[k]}
    </#list>
  </#if>
</#macro>

<@m 1 2 3 4 5 />

<@m a=1 b=2 c=3 d=4 e=5 data\-foo=6 myns\:bar=7 />

在这里插入图片描述
nested
nested 指令执行自定义指令开始和结束标签中间的模板片段。 嵌套的片段可以包含模板中任意合法的内容:插值,指令等…它在上下文环境中被执行, 也就是宏被调用的地方,而不是宏定义体的上下文中。
因此,比如, 你不能看到嵌套部分的宏的局部变量。
如果你没有调用 nested 指令, 自定义指令开始和结束标记中的部分将会被忽略

<#macro do_twice>
  1. <#nested>
  2. <#nested>
</#macro>
<@do_twice>something</@do_twice>

在这里插入图片描述
nested 指令可以对嵌套内容创建循环变量。例如:

<#macro do_thrice>
  <#nested 1>
  <#nested 2>
  <#nested 3>
</#macro>
<@do_thrice ; x>
  ${x} Anything.
</@do_thrice>

在这里插入图片描述
更为复杂的示例:

<#macro repeat count>
  <#list 1..count as x>
    <#nested x, x/2, x==count>
  </#list>
</#macro>
<@repeat count=4 ; c, halfc, last>
  ${c}. ${halfc}<#if last> Last!</#if>
</@repeat>

在这里插入图片描述
return
使用 return 指令, 你可以在任意位置留下一个宏或函数定义。比如:

<#macro test>
  Test text
  <#return>
  Will not be printed.
</#macro>
<@test/>

在这里插入图片描述


## 十五.noparse 写法:
<#noparse>
  ...
</#noparse>

描述

FreeMarker 不会在这个指令体中间寻找FTL标签, 插值和其他特殊的字符序列,除了noparse的结束标记。

Example:
--------

<#noparse>
  <#list animals as animal>
  <tr><td>${animal.name}<td>${animal.price} Euros
  </#list>
</#noparse>

在这里插入图片描述


## 十六.nt 写法:
<#nt>

描述

“不要削减”。该指令禁用行中出现的 剥离空白。 它也关闭其他同一行中出现的削减指令(t, rt,lt的效果)。


## 十七.setting 写法:
<#setting name=value>
  • name: 设置的名称。不是表达式!
  • value: 设置的新值,是表达式。

描述

为进一步的处理而设置。设置是影响 FreeMarker 行为的值。 新值仅仅在被设置的模板处理时出现,而且不触碰模板本身。 设置的初始值是由程序员设定的

比如:假设初始化的模板本地化是 de_DE (德国)。那么:

${1.2}
<#setting locale="en_US">
${1.2}
因为德国人使用逗号作为小数分隔符,而美国人使用点。

在这里插入图片描述


十七.stop

写法:

<#stop>
或
<#stop reason>

reason: 关于终止原因的信息化消息。是表达式,被算做是字符串。

描述

中止模板处理,给出(可选的)错误消息。 不要在普通情况下对结束模板处理使用! FreeMarker 模板的调用者会将它视为失败的模板呈现, 而不是普通完成的。


十八.switch, case, default, break

写法:

<#switch value>
  <#case refValue1>
    ...
    <#break>
  <#case refValue2>
    ...
    <#break>
  ...
  <#case refValueN>
    ...
    <#break>
  <#default>
    ...
</#switch>
  • value, refValue1,等: 表达式将会计算成相同类型的标量。
  • break 和 default 是可选的。

描述

这个指令的用法是不推荐的,因为向下通过的行为容易出错。使用 elseif来代替, 除非你想利用向下通过这种行为。

Switch 被用来选择模板中的一个片段,如何选择依赖于表达式的值

<#switch animal.size>
  <#case "small">
     This will be processed if it is small
     <#break>
  <#case "medium">
     This will be processed if it is medium
     <#break>
  <#case "large">
     This will be processed if it is large
     <#break>
  <#default>
     This will be processed if it is neither
</#switch>

当它选择一个 case 指令后,它就会继续处理其中的内容, 直到遇到 break 指令。也就是它遇到另外一个 case 指令或 <#default> 标记时也不会自动离开 switch 指令

<#switch x>
  <#case 1>
    1
  <#case 2>
    2
  <#default>
    d
</#switch>

如果 x 是 1,它会打印1 2 d;如果 x 是 2,那么就会打印2 d;如果 x 是 3,那么就会打印d。这就是前面提到的向下通过行为。 break 标记指示 FreeMarker 直接略过剩下的 switch 代码段


十九.t, lt, rt

写法:

<#t>

<#lt>

<#rt>

描述

这些指令,指示FreeMarker去忽略标记中行的特定的空白:

  • t (整体削减):忽略本行中首和尾的所有空白。

  • lt (左侧削减):忽略本行中首部所有的空白。

  • rt (右侧削减):忽略本行中尾部所有的空白。

这里:

“首部空白” 表示本行所有空格和制表符 (和其他根据 UNICODE 中的空白字符,除了换行符) 在第一个非空白字符之前。

“尾部空白” 表示本行所有的空格和制表符 (和其他根据 UNICODE 中的空白字符,除了换行符) 在最后一个非空白字符之后,还有 行末尾的换行符。

--
  1 <#t>
  2<#t>
  3<#lt>
  4
  5<#rt>
  6
--

在这里插入图片描述
这些指令在行内的放置不重要。也就是说,不管你是将它们放在行的开头, 或是行的末尾,或是在行的中间,效果都是一样的。


二十.User-defined directive (<@…>)

写法:

<@user_def_dir_exp param1=val1 param2=val2 ... paramN=valN/>
(注意 XML 风格, / 在 > 之前)  
或如果需要循环变量 (更多细节...)
<@user_def_dir_exp 
param1=val1 param2=val2 ... paramN=valN ; lv1, lv2, ..., lvN/>

或和上面两个相同但是使用结束标签:

<@user_def_dir_exp ...>
  ...
</@user_def_dir_exp>
或
<@user_def_dir_exp ...>
  ...
</@>

或和上面的相同但是使用位置参数传递 :

<@user val1, val2, ..., valN/>
等...
  • user_def_dir_exp: 表达式算作是自定义指令(比如宏),将会被调用。
  • param1, param2等…: 参数的名称,它们 不是 表达式。
  • val1, val2等…: 参数的值,它们 是 表达式。
  • lv1, lv2等…: 循环变量 的名称, 它们 不是 表达式。

描述

这将调用用户自定义指令,比如宏。参数的含义, 支持和需要的参数的设置依赖于具体的自定义指令。
示例1:调用存储在变量 html_escape 中的指令:

<@html_escape>
  a < b
  Romeo & Juliet
</@html_escape>

在这里插入图片描述
示例2:调用有参数的宏:

<@list items=["mouse", "elephant", "python"] title="Animals"/>
...
<#macro list title items>
  <p>${title?cap_first}:
  <ul>
    <#list items as x>
      <li>${x?cap_first}
    </#list>
  </ul>
</#macro>

在这里插入图片描述
结束标签:

你可以在 结束标签 中忽略 user_def_dir_exp。 也就是说,你可以写 </@> 来替代 </@anything>。 这个规则当表达式 user_def_dir_exp 太复杂时非常有用,因为你不需要在结束标签中重复表达式。 此外,如果表达式包含比简单变量名和点还多的表达式,你就不能再重复它们了。比如, <@a_hash[a_method()]>...</@a_hash[a_method()]> 就是错的,你必须写为 <@a_hash[a_method()]>...</@>。 但是 <@a_hash.foo>...</@a_hash.foo> 是可以的

循环变量:
一些自定义指令创建循环变量(和 list 指令相似)。
正如预定义指令(如 list)一样,当你调用这个指令 (如 <#list foos as foo>…</#list>中的 foo)时循环变量的 名称 就给定了, 而变量的 值 是由指令本身设置的。
在自定义指令的情形下,语法是循环变量的名称在分号之后给定

<@myRepeatMacro count=4 ; x, last>
  ${x}. Something... <#if last> This was the last!</#if>
</@myRepeatMacro>

请注意,由自定义指令创建的循环变量数量和分号之后指定的循环变量数量需要不匹配。 
也就是说,如果你对重复是否是最后一个不感兴趣,你可以简单来写:

<@myRepeatMacro count=4 ; x>
  ${x}. Something...
</@myRepeatMacro>

或者你可以:

<@myRepeatMacro count=4>
  Something...
</@myRepeatMacro>

二十一.visit, recurse, fallback

写法:

<#visit node using namespace>
或
<#visit node>
<#recurse node using namespace>
或
<#recurse node>
或
<#recurse using namespace>
或
<#recurse>
<#fallback>
  • node: 算作 结点变量 的表达式。
  • namespace: 命名空间,或者是命名空间的序列。 命名空间可以以命名空间哈希表(又称为根哈希表)给定, 或者可以引入一个存储模板路径的字符串。代替命名空间哈希表, 你也可以使用普通哈希表
  • visit 和 recurse 指令是用来递归处理树的。在实践中,这通常被用来 处理XML。

猜你喜欢

转载自blog.csdn.net/weixin_41056807/article/details/99413226