FreeMarker 是一个用Java编写的模板引擎,主要用来生成HTML Web页面,特别是基于MVC模式的应用程序。虽然FreeMarker具有一些编程的能力,但不像PHP,通常由Java程序准备要显示的数据,由 FreeMarker模板生成页面。 FreeMarker可以作为Web应用框架一个组件,但它与容器无关,在非Web应用程序环境也能工作的很好。 FreeMarker适合作为MVC的视图组件,还能在模板中使用JSP标记库。
< head >
< title > Welcome! </ title >
</ head >
< body >
< h1 > Welcome ${ user }! </ h1 >
< p > Our latest product:
< a href ="${ latestProduct.url }" > ${ latestProduct.name } </ a > !
</ body >
</ html >
上 面的例子中,在简单的HTML中加入了一些由${ … }包围的特定FreeMarker的指令,这个文件就称为模板了。而user、 latestProduct.url和latestProduct.name来自于数据模型,由Java程序提供,模板设计者就不用关心数据从哪来的。
FreeMarker模板中可以包括下面四种特定部分:
一.) 文本:直接输出
二.) FTL标记(FreeMarker模板语言标记):类似于HTML标记,名字前加#(有些以 at 开始,用户自定义标记)予以区分,不会输出。
字符串- 使用单引号或双引号限定;如果包含特殊字符需要转义符:${ "It's \"quoted\" andthis is a backslash: \\" }
有一类特殊的字符串:${ r"C:\foo\bar" },输出结构为:C:\foo\bar,在引号前面加r被认为是纯文本。
数字-直接输入,不需要引号。${ 08 }, ${ +8 }, ${ 8 dot 00 } and ${ 8 } 都是相同的
布尔值-true和false,不使用引号
Sequences(序列)-由逗号分隔的变量列表,由方括号限定,类似java中的一维数组:
${ x }
</ #list >
输出结果:
winter
spring
summer
autumn
例二:[2 + 2, [1, 2, 3, 4], "whatnot"]
例三:2..5,等同于[2, 3, 4, 5];5..2,等同于[5,4,3,2]。注意方括号是不需要的。
Hashes(散列)-由逗号分隔的键-值列表,由大括号限定,键和值之间用冒号分隔:{ "name":"green mouse", "price":150 },键和值都是表达式,但是键必须是字符串。
获取变量-${ variable },变量名只能是字母、数字、下划线、$、 at 和#的组合,且不能以数字开头。下列表达式是等价的:
book dot author.name
book["author"].name
book.author.["name"]
book["author"]["name"]
获取Sequence(序列)片断-使用[startindex..endindex],例如:seq中存储了"a", "b", "c", "d","e",那么seq[1..2]包含了b和c两个值。
可以使用.variablename语法访问FreeMarker内置变量。
字符串操作
{ "Hello ${ user }!" } <==> ${ "Hello " + user + "!" }
${ "${ user }${ user }${ user }${ user }" } <==> ${ user + user + user + user }
${ … }只能在文本中使用,下面是错误的代码:
<#if ${ isBig }>Wow!</#if>
<#if "${ isBig }">Wow!</#if> //此处的代码也是错误的,因为if指令需要的是boolean,实际的却是个字符串
子字符串的操作,假设user的值为"Big Joe":
${ user[0] }${ user[4] } <==> BJ
${ user[1..4] } <==> ig J
Sequences(序列)操作
- ${ user }
</ #list >
结果:
- Joe
- Fred
- Julia
- Kate
Hashes(散列)操作
- Joe is ${ ages.Joe }
- Fred is ${ ages.Fred }
- Julia is ${ ages.Julia }
结果:
- Joe is 30
- Fred is 25
- Julia is 18
算术运算
${ x * x - 100 }
${ x / 2 }
${ 12 % 10 }
结果:
-75
2.5
2
注意: 操作符两边必须是数字;使用"+"时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串。
使用内建的指令int获得整数部分:
${ 1.1?int }
${ 1.999?int }
${ -1.1?int }
${ -1.999?int }
结果:
2
1
1
-1
-1
比较操作符-<#if expression>...</#if>
1.)使用=(或==,完全相等)测试两个值是否相等,使用!= 测试两个值是否不相等
2.)=和!=两边必须是相同类型的值,否则会产生错误,例如<#if 1 = "1">会引起错误
3.)Freemarker是精确比较,所以"x"、"x "和"X"是不相等的
4.)对数字和日期可以使用<、<=、>和>=,但不能用于字符串
5.) 由于Freemarker会将>解释成FTL标记的结束字符,所以对于>和>=可以使用括号来避免这种情况,例如<#if (x > y)>,另一种替代的方法是,使用lt、lte、gt和gte来替代<、<=、>和>=
逻辑操作符-&&(and)、||(or)、!(not),只能用于布尔值,否则会产生错误
<#if x < 12 && color = "green">
We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
It's not hot.
</#if>
内置函数-用法类似访问hash(散列)的子变量,只是使用"?"替代".",例如:user?upper_case
下面列出常用的一些函数:
对于字符串
html-对字符串进行HTML编码
cap_first-使字符串第一个字母大写
lower_case-将字符串转换成小写
trim-去掉字符串前后的空白字符
对于Sequences(序列)
size-获得序列中元素的数目
对于数字
int-取得数字的整数部分(如-1.9?int的结果是-1)
例一:
${ test?html }
${ test?upper_case?html }
结果:
Tom & Jerry
TOM & JERRY
例二:
${ seasons?size }
${ seasons[1]?cap_first } < #-- left side can by any expression -- >
${ "horse"?cap_first }
结果:
4
Spring
Horse
方法的调用
${ repeat("What", 3) }
${ repeat(repeat("x", 2), 3) + repeat("What", 4)?upper_case }
结果:
WhatWhatWhat
xxxxxxWHATWHATWHATWHAT
操作符优先顺序
后缀 [subvarName] [subStringRange] . (methodParams)
一元 +expr、-expr、!
内建 ?
乘法 *、 / 、%
加法 +、-
关系 <、>、<=、>=(lt、lte、gt、gte)
相等 =、!=
逻辑 &&
逻辑 ||
数字范围 ..
三.) Interpolation:由${ ... }或#{ ... }两种类型,输出计算值,可以定义输出的格式
例一:
< #assign answer =42/>
${ answer }
${ answer?string } <#-- the same as ${ answer } -- >
${ answer?string.number }
${ answer?string.currency }
${ answer?string.percent }
结果:
$42.00
$42.00
42
$42.00
4,200%
例二:
${ lastUpdated?string("EEE, MMM d, ''yy") }
${ lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'") }
结果:
2003-04-08 21:24:44 Pacific Daylight Time
Tue, Apr 8, '03
Tuesday, April 08, 2003, 09:24:44 PM (PDT)
例三:
${ foo?string("yes", "no") }
结果:
yes
例四:
#{ x; M2 } < #-- 2.58 -- >
#{ y; M2 } < #-- 4 -- >
#{ x; m1 } < #-- 2.6 -- >
#{ y; m1 } < #-- 4.0 -- >
#{ x; m1M2 } < #-- 2.58 -- >
#{ y; m1M2 } < #-- 4.0 -- >
说明:mX-小数部分最小X位;MX-小数部分最大X位。
四.) 注释:<#--和-->
下面是一个常用的模板例子:
< table border =1>
<tr >< th > Name < th > Price
< #list animals as being >
< tr >
< td >
< #if being.size = "large" >< b ></ #if >
${ being.name }
< #if being.size ="large" ></ b ></ #if >
< td > ${ being.price } Euros
</ #list >
</ table >
< #include "/copyright_footer.html" >
注意点:
1.) FreeMarker是区分大小写的;
2.) FTL标记不能位于另一个FTL标记内部,例如:<#if <#include 'foo'>='bar'>...</if>;
3.) ${ … }只能在文本中使用;
4.) 多余的空白字符会在模板输出时去除;
5.) 如果使用的指令不存在,会产生一个错误消息。
有两种不同的类型:Macro(宏)和transform(传递器),Macro是在模板中使用macro指令定义,而transform是在模板外由程序定义(基本上都是基于Java的),这里通过Macro来介绍自定义指令。
例一:
<#macro greet>
<font size="+2">Hello Joe!</font>
</#macro>
使用:<@greet>< /@greet> 或 <@greet/>
结果:<font size="+2">Hello Joe!</font>
参数-在macro指令中可以在宏变量之后定义参数
例二:
<#macro greet person>
<font size="+2">Hello ${person}!</font>
</#macro>
使用:<@greet person="Fred"/> and <@greet person="Batman"/>
结果: <font size="+2">Hello Fred!</font> and <font size="+2">Hello Batman!</font>
macro可以有多个参数,参数的次序是无关的,在macro指令中只能使用定义的参数,并且必须对所有参数赋值,可以在定义参数时指定缺省值:
< font size ="+2" color ="${color}" > Hello ${person}! </ font >
</ #macro >
在自定义指令嵌套内容:模板片断中使用<#nested>指令
< table border =4 cellspacing =0 cellpadding =4><tr><td>
<#nested >
</ tr ></ td ></ table >
</ #macro >
使用:<@border>The bordered text</@border>
结果:
<tr >< td > The bordered text
</ tr ></ td ></ table >
<#nested>指令可以被多次调用:
< #nested >
< #nested >
< #nested >
</ #macro >
使用:
<@do_thrice>Anything.</@do_thrice>
结果:
Anything.
Anything.
Anything.
注意:嵌套内容是无法访问到macro中的局部变量的。
例如:
< #local y = "test" >
< #list 1..count as x >
${y} ${count}/${x}: < #nested >
</ #list >
</ #macro >
< @repeat count =3>${y?default("?")} ${x?default("?")} ${count?default("?")}</@repeat >
结果:
test 3/1: ? ? ?
test 3/2: ? ? ?
test 3/3: ? ? ?
下面是一个嵌套使用自定义指令的例子:
< ul >
< @do_thrice >
< li >< @greet person ="Joe" />
</ @do_thrice >
</ ul >
</ @border >
结果:
<ul >
< li >< font size ="+2" > Hello Joe! </ font >
< li >< font size ="+2" > Hello Joe! </ font >
< li >< font size ="+2" > Hello Joe! </ font >
</ ul >
</ tr ></ td ></ table >
在macro中使用循环变量-作为nested指令的参数传递循环变量的实际值,而在调用用户定义指令时,在<@…>开始标记的参数后面指定循环变量的名字:
< #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!
注意:循环变量和用户定义指令开始标记指定的数目可以不同,调用时少指定循环变量,则多指定的值不可见,调用时多指定循环变量,多余的循环变量不会被创建。
模板中的变量,有三种类型:
1.) plain(全局)变量:可以在模板的任何地方访问,包括使用include指令插入的模板,使用assign指令创建和替换
2.) 局部变量:在macro中有效,使用local指令创建和替换
3.) 循环变量:只能存在于指令的嵌套内容,由指令(如list)自动创建;宏的参数是局部变量,而不是循环变量
用assign指令创建和替换的例子:
${x}
< #assign x = x + 3 > < #-- replace variable x -- >
${x}
结果:
1
4
局部变量隐藏(而不是覆盖)同名的plain变量;循环变量隐藏同名的局部变量和plain变量,下面是一个例子:
1. ${x} < #-- we see the plain var. here -- >
< @test />
6. ${x} < #-- the value of plain var. was not changed -- >
< #list ["loop"] as x >
7. ${x} < #-- now the loop var. hides the plain var. -- >
< #assign x = "plain2" > < #-- replace the plain var, hiding does not mater here -- >
8. ${x} < #-- it still hides the plain var. -- >
</ #list >
9. ${x} < #-- the new value of plain var. -- >
< #macro test >
2. ${x} < #-- we still see the plain var. here -- >
< #local x = "local" >
3. ${x} < #-- now the local var. hides it -- >
< #list ["loop"] as x >
4. ${x} < #-- now the loop var. hides the local var. -- >
</ #list >
5. ${x} < #-- now we see the local var. again -- >
</ #macro >
结果:
1. plain
2. plain
3. local
4. loop
5. local
6. plain
7. loop
8. loop
9. plain2
内部循环变量隐藏同名的外部循环变量,例如:
${x}
< #list ["loop 2"] as x >
${x}
< #list ["loop 3"] as x >
${x}
</ #list >
${x}
</ #list >
${x}
</ #list >
结果:
loop 1
loop 2
loop 3
loop 2
loop 1
模板中的变量会隐藏(而不是覆盖)数据模型中同名变量,如果需要访问数据模型中的同名变量,使用特殊变量global,下面的例子假设数据模型中的user的值是Big Joe:
${user} < #-- prints: Joe Hider -- >
${.globals.user} < #-- prints: Big Joe -- >