概要
在FreeMarker中最简单的模板文件是一个纯 HTML 文件 (或者别的文本文件,FreeMarker并不将模板文件限于 HTML)。 当用户端访问这个页面的时候就将其发给用户端就好了,不过当你想让你的模板文件具有更多的动态性的时候,你只需要将FreeMarker能懂的特殊部分加到 HTML 文件就OK了,这些个部分包含以下三大类:
- ${ ... } : FreeMarker会用花括号里边变量所持有的值将这部分全部替换掉,这样就组成了输出文件。我们称这部分的名称为插值 (interpolations)。 可以看前面的例子
- FTL tags (FreeMarker Template Languages tags 的简称) : FTL tags 和 HTML 存在相似性,不过他们是 FreeMarker 中的指令并且是不会出现在输出文件中。他们的名字是以 # 打头的。 (在 FreeMarker 中还有中 FTL tags 是用户的, 它们以 @ 开头,是一个更高的主题了,后边会涉及到)
- 注释 : 这个注释和 HTML 中的也是类似的,不过它们是以 <#-- 和 -->分隔的。任何在分隔符合分隔符中间的部分都会被 FreeMarker 忽略掉的,同样是不会出现在输出文件中。
在 FreeMarker 中只要不是插值、 FTL 标签、注释都是被当作静态文本,不会被 FreeMarker 解析,会被原样写到输出中。
FTL 标签也可以被说成是指令 (directives)。其实它俩的关系和 HTML 中的 标签 (tags, e.g.: <table> and </table>) 和元素 (elements, e.g.: tableelement)之间的关系类似。(如果你没觉得这俩孩子有什么不同可以 把 FTL 标签和 指令当作是同义词)
指令例子
虽然 FreeMarker 有很多的指令,在这里我们值介绍三个常用的指令。
if 指令
用 if 指令就可以根据条件忽视掉模板中的指定部分。举个例子,比如前边的例子中你想将自己的老板, Big Joe, 与其他的用户区别开来:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>
Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
这个例子中,你相当于告诉 FreeMarker 当变量 user 的值是 "Big Joe"时那么 ", out beloved leader" 将会出现在那里。根据规则,当 condition的只是 false 的时候 <#if condition> 和 </#if> 之间的部分是会被忽略掉的。
让我们详细看看 condition 的用法 : == 是用来测试其左边和右边的值是否相等,最终的结果是一个boolean值,相应的值就是 true or false。在 == 的左边我引用了一个我们已经比较熟悉其语法的变量,这部分会被变量的值替换掉。这里要注意下,在指令或是 插值中没有用被单、双引号扩起来的值都被视为引用之的变量。右边,我指定了一个字符串,在 FreeMarker 中字符串是必须处在引号里边的。
当 price 的值为 0 的时候,下边这个例子是会输出 “Pythons are free today!”:
<#if animals.python.price == 0>
Pythons are free today!
</#if>
就像前边的字符串一样,这里的数字也是被直接指定了,当然 0 是没有被引号包围。如果你写成这样 "0",那么 FreeMarker 就会误解成字符串。
下边这个例子,当 price 不等于 0 的时候会打印 "Pythons are not free today!":
<#if animals.python.price != 0>
Pythons are not free today!
</#if>
就像你所猜的, != 代表不等于。
同样你也可以这么写: (数据来自这里)
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
</#if>
在使用 <#else> 时,可以显示当条件为 false 时指定的内容。如下例:
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
<#else>
Pythons are not cheaper than elephants today.
</#if>
这个例子中当 elephant 的 price 小于 python 的 price 时会打印 "Pythons are not cheaper than elephant today." 否则就会打印另外一句了。
当一个变量的值是 boolean 类型的时候,那么它就可以直接 用在 if 的 condition 部分 :
<#if animals.python.protected>
Warning! Pythons are protected animals!
</#if>
list 指令
这个指令在需要穷举某些数据的时候很有用。 比如你打算将之前例子中的数据和下边的模板结合起来:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr><td>${being.name}<td>${being.price} Euros
</#list>
</table>
那么经过解析后,结果就是这样了:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<tr><td>mouse<td>50 Euros
<tr><td>elephant<td>5000 Euros
<tr><td>python<td>4999 Euros
</table>
list 指令的一般格式:
<#list sequenceasloopVariable>repeatThis</#list>
当每次穷举 sequence 中的值时,每次穷举一个都会重复一次输出 repeatThis 。在所有的穷举中, loopVariable 变量会持有当前的穷举值, 并且这个变量只会存在于 <#list ..> 和</#list> 之间,这点和局部变量的生命周期比较类似的。
在下边的例子中我们将 data-model 里边的 fruits 穷举出来:
<p>And BTW we have these fruits:
<ul>
<#list whatnot.fruits as fruit>
<li>${fruit}
</#list>
<ul>
whatnot.fruits 表达式你应该是比较熟悉了,在 这里我们有提到过。
include 指令
用include指令你就可以将另一个文件插入到模板中了。
假如你想在多个页面上显示同样的版权信息,那么这时我们就只需要创建一个包含版权信息的文本,在任何需要这个信息的文件中插入就好了。比如我们将信息写入 copyright_footer.html :
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
无论在什么地方,你只需要在需要的时候将其插入即可:
<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<#include "/copyright_footer.html">
</body>
</html>
那么他会输出如下内容:
<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
</body>
</html>
而且当你在改变了版权文件后,用户访问时在所有的页面上看到的信息都是更改过后的了。
指令的组合使用
指令的使用是比较灵活的,只要你愿意,一个页面上的指令可以多次使用,并且他们之间是可以相互嵌套的,这点和 HTML 的元素之间的嵌套一样。比如下例,会显示所有动物的名称并且会将大型动物的名字也给设置为大字体:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr>
<td>
<#if being.size == "large"><font size="+1"></#if>
${being.name}
<#if being.size == "large"></font></#if>
<td>${being.price} Euros
</#list>
</table>
我们要注意一点,因为 FreeMarker 对于在 FTL 标签、插值、注释外的静态文本是不会进行解析的,所以像上边的 font 这类信息对其是不可见的,其他内容类似。
处理丢失的变量
在实际开发中的 data-model 是包含了许多可选的变量(i.e, 信息丢失)。为了早点的发现一些典型的人为失误, FreeMarker 一般是不会容忍变量的丢失除非显示告诉它当它碰到类似情况后的处理方式,目前是主要有两种典型的情况:
程序员需要特别注意: 对于 FreeMarker 来说一个不存在的变量和 null 值的变量是同样的概念,所以 “丢失” 就意味着这两种情况。
无论何时使用变量,以防万一其值丢失了,可以通过在变量后边跟上感叹号 (!) 和默认值的组合值的方式来阻止抛出异常。就像下边的例子一样,当 user 这个变量从 data-model 中丢失的时候,模板本身好像是没有注意到一样,并且会将 字符串 “Anonymous” 作为 user 的值。(同样的,当 user 的值存在的时候它会当 !"Anonymous"不存在一样)
<h1>Welcome ${user!"Anonymous"}!</h1>
我们也可以使用 ?? 来判断某个变量的值是否存在,它如同 !一样跟在变量后就好了。当和 if 组合使用时,当变量 user丢失的时候那么整个的问候语句会被忽略的。
<#if user??><h1>Welcome ${user}!</h1></#if>
我们还要考虑具有多层次解析的情况,比如 animals.python.price,当判断默认值的写法为这样的时候 animals.python.price!0,要确保其不报错那么只能是当 animals.python 本身是存在的才行,也只有 price 是可以丢失的。 因此,当 animals 和 python 中任何一个出错时解析都会停止并且报 "undefined variable" 异常。针对这种情况的解决方案是, (animals.python.price)!0,这样在三个变量中任何一个变量丢失都会给出默认值 0 ,这招对 ?? 也是同样管用的。