FreeMarker 模版引擎

哈希表是一个容器,里面存储的是键值对。
如果仅存储单值的变量被称之为标量。
FreeMarker指令是不会直接输出东西的。这些标签的使用一般以符号#开头。用户自定义的FTL标签使用@符号来代替#。
其他任何不是FTL标签,插值或注释的内容将被视为静态文本,这些东西就不会被FreeMarker所解析,会被按照原样输出出来。
Comments注释:FreeMarker的注释和HTML的注释相似,但是它用<#--和-->来分隔。任何介于这两个分隔符(包含分隔符本身)之间内容会被FreeMarker忽略,就不会输出出来了。

<#if animals.python.price<animals.elephant.price>
  oh my god
<#else>
  that is nomal!
</#if>

当条件不满足时if指令之间的内容不再被显示。
freemarker只解析特殊的指令和标签,其他的按原样输出。
<h1>
  welcome ${user}<#if user=="zengshaotao"> our   leader</#if>
</h1>

如果变量是布尔值(true或者false),那么就可以直接让其作为if的条件condition:
<#if animals.python.protected>
Warning! Pythons are protected animals!
</#if>

当需要遍历集合的内容时,list指令是相当好用的。
list指令的一般格式为:
<#list sequence as loopVariable>repeatThis</#list>
repeatThis部分将会在给定的sequence遍历时的每项中重复,从第一项开始,一个接着一个。所有的重复中loopVariable将持有当前项的值。这个循环变量仅存在于<#list …>和</#list>标签之间。
<#list animals as animal>
<li>${animal}
</#list>

使用include指令,我们可以插入其他文件内容到当前的模板中
<#include "/copyright_footer.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标签,即html标签。

通过在变量名后面跟着一个!和默认值来避免变量丢失这种情况。就${user!"oh"}从数据模型中丢失时,模板将会将user的值表现为字符串”oh”。(若user并没有丢失,那么模板就表现出”oh”不存在一样)

当然也可以通过放置??在变量名后面来询问FreeMarker一个变量是否存在.<#if user??><h1>Welcome ${user}!</h1></#if>

关于多级访问的变量,比如animals.python.price,书写代码:animals.python.price!0,仅当animals.python存在而仅仅最后一个子变量price可能不存在(这种情况下我们假设价格是0)。如果animals或者python不存在,那么模板处理将会以“未定义的变量”错误停止。为了防止这种情况的发生,可以这样来书写代码(animals.python.price)!0。这种情况下当animals或python不存在时表达式的结果仍然是0。对于??也是同样用来的处理这种逻辑的:animals.python.price??对比(animals.python.price)??

要注意一个数值也可有多种类型,对于一个数值可能存在哈希表和序列这两种类型,这时,该变量就支持索引和名称两种访问方式。不过容器基本是当作哈希表或者序列来使用的,而不是两者同时使用

一个值是方法或函数的时候那么它就可以计算其他的值,结果取决于传递给它的参数。

在数据模型中放置了一个方法变量avg,那么它就可以被用来计算数字的平均值。给定3和5作为参数,访问avg时就能得到结果4

用户自定义指令是一种子程序,一种可以复用的模板代码段

插值仅仅可以在文本中间使用(也可以在字符串表达式中)

,指令有两种类型:预定义指令和用户自定义指令。对于用户自定义的指令使用@来代替#,比如<@mydirective parameters>...</@mydirective>。更深的区别在于如果指令没有嵌套内容,那么必须这么使用<@mydirective parameters />

。如果文本本身包含用于字符引用的引号(双引号”或单引号’)或反斜杠时,应该在它们的前面再加一个反斜杠,这就是转义。转义允许你直接在文本中输入任何字符,包括反斜杠

注意字符序列${(#{)有特殊的含义,它们被用做插入表达式的数值(典型的应用是:"Hello ${user}!")。如果想要打印${,就要使用下面所说的原生字符串。
一种特殊的字符串就是原生字符串。在原生字符串中,反斜杠和${没有特殊的含义,它们被视为普通的字符。为了表明字符串是原生字符串,在开始的引号或单引号之前放置字母r,例如:
${r"${foo}"}
${r"C:\foo\bar"}
将会打印:
${foo}
C:\foo\bar

<#list ["winter", "spring", "summer", "autumn"] as x>
${x}
</#list>

 

 

=或!=两边的表达式的结果都必须是标量,而且两个标量都必须是相同类型

可以使用lt代替<,lte代替<=,gt代替>,gte代替>=

逻辑操作符仅仅在布尔值之间有效,若用在其他类型将会产生错误导致模板执行中止

内建函数以?形式提供变量的不同形式或者其他信息。使用内建函数的语法和访问哈希表子变量的语法很像,除了使用?号代替点,其他都一样。例如得到字符串的大写形式:user?upper_case。

内建函数html: 字符串中所有的特殊HTML字符都需要用实体引用来代替(比如<代替&lt;)
${test?html}
${test?upper_case?html}
假设字符串test存储”Tom & Jerry”,那么输出为
Tom &amp; Jerry
TOM &amp; JERRY

${seasons?size}
${seasons[1]?cap_first} <#-- left side can by any expression -->
${"horse"?cap_first}
假设seasons存储了序列"winter", "spring", "summer", "autumn",那么上面的输出将会是:
4
Spring
Horse

可以使用方法调用操作来使用一个已经定义的方法。方法调用的语法形式是使用逗号来分割在括号内的表达式而形成的参数列表,这些值就是参数。方法调用操作将这些值传递给方法,然后返回一个结果,这个结果就是整个方法调用表达式的值。

为不存在的变量进行处理
${mouse!"No mouse."}
<#assign mouse="Jerry">
${mouse!"No mouse."}

:unsafe_expr??或(unsafe_expr)??
这个操作符告诉我们一个值是否存在。基于这种情况,结果是true或false。

 

 

插值表达式的结果必须是字符串,数字或日期类型的,因为只有数字和日期类型可以自动转换为字符串类型,其他类型的值(如布尔,序列)只能手动转换为字符串类型,否则就会发生错误导致模板执行中止。

若要使用插值方式来打印布尔值会引起错误,中止模板的执行。例如:${a == 2}就会引起错误,不会打印”true”或其他内容。

我们可以使用内建函数string来将布尔值转换为字符串形式。比如打印变量”married”(假设它是布尔值),那么可以这么来写: ${married?string("yes", "no")}。

自定义指令可以使用macro指令来定义.宏是有一个变量名的模板片段.

<#macro greet>
<font size="+2">Hello Joe!</font>
</#macro>

在<#macro greet>和</#macro>之间的内容(称为宏定义体).可以在FTL标记中通过@代替#来使用自定义指令。使用变量名作为指令名。而且,自定义指令的结束标记也是需要的。那么,就可以这样来使用greet了:<@greet></@greet>或者<@greet/>
输出的如下内容
<font size="+2">Hello Joe!</font>是给客户端浏览器的。

宏能做的还有很多,因为在<#macro ...>和</#macro>之间的东西是模板片段,也就是说它可以包含插值(${...})和FTL标签(如<#if ...>...</#if>)。

我们来改进greet宏使之可以使用任意名字,而不仅仅是”Joe”。为了实现这个目标,就要使用到参数。在macro指令中,宏名称的后面位置是用来定义变量的。这里我们仅在greet宏中定义一个变量, person:
<#macro greet person>
<font size="+2">Hello ${person}!</font>
</#macro>
那么就可以这样来使用这个宏:
<@greet person="Fred"/> and <@greet person="Batman"/>
这和HTML的语法是很相似的,它会打印出:
那么就看到了,宏参数的真实值是可以作为变量(person)放在宏定义体中的。使用预定义指令,参数的值(=号后边的值)可以是FTL表达式。这样,不像HTML,"Fred"和"Batman"的引号就可以不用要了。<@greet person=Fred/>也意味着使用变量的值Fred作为person参数,而不是字符串"Fred"。当然参数值并不一定是字符串类型,也可以是数字,布尔值,哈希表,序列等…也可以在=号左边使用复杂表达式(比如someParam=(price + 50)*1.25)。
自定义指令可以有多个参数。如下所示,再添加一个新参数color:
那么,这个宏就可以这样来使用:
<@greet></@greet>
<@greet/>
<font size="+2">Hello Joe!</font>
<#macro greet person>
<font size="+2">Hello ${person}!</font>
</#macro>
<@greet person="Fred"/> and <@greet person="Batman"/>
这和HTML的语法是很相似的,它会打印出:
<font size="+2">Hello Fred!</font>
and <font size="+2">Hello Batman!</font>

自定义指令可以有多个参数。如下所示,再添加一个新参数color:
<#macro greet person color>
<font size="+2" color="${color}">Hello ${person}!</font>
</#macro>
这个宏就可以这样来使用:
<@greet person="Fred" color="black"/>
参数的顺序不重要,下面这个和上面的也是相同的。
<@greet color="black" person="Fred"/>
使用时不能使用多余的参数。

使用默认宏变量的定义:
<#macro greet person color="black">
<font size="+2" color="${color}">Hello ${person}!</font>
</#macro>

我们这么使用宏就可以了:<@greet person="Fred"/>,因为它和<@greet person="Fred" color="black"/>是相同的,这样参数color的值就是已知的了。如果想给color设置为”red”,那么就写成:,这时macro指令就会使用这个值来覆盖之前设置的通用值,参数color的值就会是”red”了。

自定义指令可以嵌套内容,和预定义指令相似:<#if ...>nested content</#if>。比如,下面这个例子中是创建了一个可以为嵌套的内容画出边框:
<#macro border>
<table border=4 cellspacing=0 cellpadding=4><tr><td>
<#nested>
</tr></td></table>
</#macro>
<#nested>指令执行位于开始和结束标记指令之间的模板代码段。如果这样写:
<@border>The bordered text</@border>
将会输出如下内容:
<table border=4 cellspacing=0 cellpadding=4><tr><td>
The bordered text

nested指令也可以多次被调用,例如:
<#macro do_thrice>
<#nested>
<#nested>
<#nested>
</#macro>
<@do_thrice>
Anything.
</@do_thrice>
输出:
Anything.
Anything.
Anything.
</td></tr></table>

如果不使用nested指令,那么嵌套的内容就不会执行,如果不小心将greet指令写成了这样:
<@greet person="Joe">
Anything.
</@greet>
那么Anything会被忽略。

内嵌效果需要在定义宏变量时就指定<#nested>

<#macro repeat count>
<#local y = "test">
<#list 1..count as x>
${y} ${count}/${x}: <#nested>
</#list>
</#macro>
<@repeat count=3>${y!"?"} ${x!"?"} ${count!"?"}</@repeat>
打印内容
test 3/1: ? ? ?
test 3/2: ? ? ?
test 3/3: ? ? ?

 

 像list这样的预定义指令可以使用循环变量,自定义指令也可以有循环变量.

<#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>
输出:
1. 0.5
2. 1
3. 1.5
4. 2 Last!

在使用的时候才给定参数。

当运行FTL模板时,就会有使用assign和macro指令创建的变量的集合(可能是空的)。像这样的变量集合被成为namespace命名空间。在简单的情况下可以只使用一个命名空间,称之为main namespace主命名空间。因为通常只使用本页上的命名空间,所以就没有意识到这点。
如果想创建可以重复使用的宏,函数和其他变量的集,使用多个命名空间是必然的,通常用术语来说就是引用library库。只要考虑你在一些项目中,或者想和他人共享使用的时候,你是否有一个很大的宏的集合。但要确保库中没有宏(或其他变量)名和数据模型中变量同名,而且也不能和模板中引用其他库中的变量同名。通常来说,变量因为名称冲突也会相互碰撞。所以要为每个库中的变量使用不同的命名空间。

zst.ftl文件有如下宏的定义:
<#macro copyright date>
    <p>copyright(C) ${date} zengshaotao </p>
</#macro>
<#assign mail ="jsmith@huateng.com">
要想在zzz.ftl模版文件使用,可以选择使用<#include>指令,但是这个时候就会在引用该包含指令的文件里创建两个隐藏的变量(一个是宏,一个是变量),所以存在变量被覆盖的问题,而使用import指令就会以新的数据模型来呈现。

假定zzz.ftl文件内容如下:
<#import "/lib/my_test.ftl" as my> <#-- the hash called "my" will be the "gate" -->
<@my.copyright date="1999-2002"/>
${my.mail}
要注意它是怎么访问为lib/my_test.ftl创建的命名空间中的变量的,通过新创建的哈希表,my。那么将会打印出:
<p>Copyright (C) 1999-2002 Julia Smith. All rights reserved.</p>
jsmith@acme.com

修改zst.ftl文件的宏定义如下:
<#macro copyright date>
<p>Copyright (C) ${date} Julia Smith. All rights reserved.
<br>Email: ${mail}</p>
</#macro>

修改zzz.ftl文件内容如下:
<#import "/lib/my_test.ftl" as my>
<#assign mail="fred@acme.com">
<@my.copyright date="1999-2002"/>
${my.mail}
${mail}
将会输出:
<p>Copyright (C) 1999-2002 Julia Smith. All rights reserved.
<br>Email: jsmith@acme.com</p>
jsmith@acme.com
fred@acme.com

偶尔想要在一个被包含的命名空间上创建或替换一个变量。那么可以使用assign指令在完成,这个时候需要用到了它的namespace变量,也即是哈希变量。
<#import "/lib/my_test.ftl" as my>
${my.mail}
<#assign mail="jsmith@other.com" in my>
${my.mail}

在通常一些应用中,你也许想在模板中创建所有命名空间都可见的变量,就像数据模型中的变量一样。但是你不能在模板中改变数据模型,却可以通过global指令来达到相似的效果。

命名空间由使用的import指令中所写的路径来识别。如果想多次import这个路径,那么只会为第一次的import引用创建命名空间执行模板。后面相同路径的import只是创建一个哈希表当作访问相同命名空间的“门“。也就是相当于句柄。虽然是不同的门,但是访问的都是相同的变量。改变一个就全都改变了。

还要注意命名空间是不分层次的,它们相互之间是独立存在的。那么,如果在命名空间N1中import命名空间N2,那N2也不在N1中,N1只是可以通过哈希表来访问N2。这和在主命名空间中importN2,然后直接访问命名空间N2是一样的过程。

替换语法(方括号)和默认语法(尖括号)在一个模板中是相互排斥的。那就是说,整个模板要么全使用替换语法,要么全使用默认语法。如果模板使用了代替语法,那么如<#if ...>这样的东西就会被算作是静态文本,而不是FTL标签。类似地,如果模板使用默认语法,那么如[#if ...]这样的也会被算作是静态文本,而不是FTL标签。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值