Freemarker 语法简介

来源:互联网 发布:淘宝返利是怎么回事 编辑:程序博客网 时间:2024/05/17 21:05

FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML 网页、电子邮件、配置文件、源代码等)的通用工具。

选择 freemarker 的原因:
  1. 性能。velocity 应该是最好的,其次是 jsp,普通的页面 freemarker 性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker 的性能比使用 tag 和 el 的 jsp 好。
  2. 宏定义比 jsp tag 方便
  3. 内置大量常用功能。比如 html 过滤,日期金额格式化等等,使用非常方便
  4. 支持 jsp 标签
  5. 可以实现严格的 mvc 分离
FreeMarker 模板文件主要由如下 4 个部分组成:
  1. 文本:直接输出的部分。
  2. 注释:<#-- ... —>格式部分,不会输出。
  3. 插值:即${…}#{…}格式的部分,将使用数据模型中的部分替代输出。
  4. FTL 指令:FreeMarker 指定,和 HTML 标记类似,名字前加#予以区分,不会输出。
例子
<html>    <head>        <title>Welcome!</title>    </head>    <body>        <#-- 注释部分 -->        <#-- 下面使用插值 -->        <h1>Welcome ${user}!</h1>        <p>We have these animals:        <ul>            <#-- 使用FTL指令 -->            <#list animals as being>            <li>${being.name} for ${being.price} Euros            </#list>        </ul>    </body></html>

一、插值规则

FreeMarker 的插值有如下两种类型:

  1. 通用插值${expr}
  2. 数字格式化插值:#{expr}#{expr;format}

1. 通用插值

  • 输出 ${book.name}
  • 空值判断:
    • ${book.name?if_exists}
    • ${book.name?default(‘xxx’)} //默认值xxx
    • ${book.name!"xxx"} //默认值xxx
  • 日期格式:${book.date?string('yyyy-MM-dd')}
  • 数字格式:
    • ${book?string.number} //20
    • ${book?string.currency} //$20.00
    • ${book?string.percent} //20%

. 数字格式化插值

数字格式化插值可采用#{expr;format}形式来格式化数字。

其中format可以是:

  • mX: 小数部分最小 X 位
  • MX: 小数部分最大 X 位

示例:

<#assign x=2.582/><#assign y=4/>#{x; M2}#{y; M2}#{x; m2}#{y; m2}#{x; m1M2}#{x; m1M2} 

输出:

2.5842.584.002.582.58

二、FTL 指令规则

在 FreeMarker 中, 使用 FTL 标签来使用指令, FreeMarker 有 3 种 FTL 标签, 这和 HTML 标签是完全类似的.

  1. 开始标签:<#directivename parameter>
  2. 结束标签:</#directivename>
  3. 空标签:<#directivename parameter/>

1. 遍历 List 集合

<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"]  as item>${item}</#list>

这里会逐个输出星期几。

此外,迭代集合对象时,还包含两个特殊的循环变量:

  • item_index: 当前变量的索引值
  • item_has_next: 是否存在下一个对象

也可以使用<#break>指令跳出迭代。

2. 遍历 map 集合

{"语文":78, "数学":80}
Map 对象的 key 和 value 都是表达式,但是 key 必须是字符串
<#list map?keys as key>    ${key}=${map[key]}<br/></#list>

3. 逻辑判断

if 判断

判断条件,当 condition 的判断结果为false(布尔值)时,在<#if condition></#if>标签之间的内容将会被略过。

<#if animals.python.price < animals.elephant.price>Pythons are cheaper than elephants today.<#else>Pythons are not cheaper than elephants today.</#if>

需要注意的是比较运算符两边的类型必须相同,不能一个是 数字,一个是字符串,否则将会报错。

Switch 判断

<#switch value>    <#case refValue1>        ...        <#break>    <#case refValue2>        ...        <#break>    <#case refValueN>        ...        <#break>    <#default>        ...</#switch>

4. 空值处理

FreeMarker 对空值的处理非常严格,FreeMarker 的变量必须有值,没有被赋值的变量就会抛出异常,因为 FreeMarker 未赋值的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误。这里所说的空值,实际上也包括那些并不存在的变量,对于一个 Java 的null值而言,我们认为这个变量是存在的,只是它的值为null,但对于 FreeMarker 模板而言,它无法理解null值,null值和不存在的变量完全相同。

为了处理缺失变量, FreeMarker 提供了两个运算符:

  • !: 指定缺失变量的默认值
  • ??: 判断某个变量是否存在

指定缺失变量的默认值

!运算符的用法有如下两种:

variable!variable!defaultValue

第一种用法不给缺失的变量指定默认值,表明默认值是空字符串、长度为 0 的集合或者长度为 0 的 Map 对象。

另外,使用!指定默认值时,并不要求默认值的类型和变量类型相同。

Demo:

<h1>Welcome ${user!"Anonymous"}!</h1><#assign user = "Doe Joe"><h1>Welcome ${user!"Anonymous"}!</h1>
输出:
<h1>Welcome Anonymous!</h1><h1>Welcome Doe Joe!</h1>

判断某个变量是否存在

使用??运算符非常简单,它总是返回一个布尔值,用法为:variable??,如果该变量存在,返回true,否则返回false

但是需要注意的是,我们在使用 freemarker 获取后端回填数据时,必须使用??来判断是否存在,再进行使用。或者通过 default 来赋予默认值,否则一旦数据不存在,就会出现前端报错完全崩溃的情况。

Demo:

<#if mouse??>Mouse found.<#else>No mouse found.</#if>
实际使用示例:
<#if !productList??><div class="n-result">    <h3>暂无内容!</h3></div><#else><div class="n-plist">    <ul class="f-cb" id="plist">        <#list productList as x>            <#if !x.isBuy>                <li id="p-${x.id}">                    <a href="/show?id=${x.id}" class="link">                        <div class="img"><img src="${x.image}" alt="${x.title}"></div>                        <h3>${x.title}</h3>                        <div class="price"><span class="v-unit">¥</span><span class="v-value">${x.price}</span></div>                    </a>                </li>            </#if>        </#list>    </ul></div></#if>

5. 变量的声明

plain 变量

它能从模板中的任何位置来访问,或者从使用 include 指令引入的模板访问。可以使用assignmacro指令来创建或替换这些变量。

<#assign num=0/><#assign x="Hello ${user}!"/>

局部变量

只能在#function#macro定义体中定义 & 有效,使用 local 指令创建和替换。

局部变量会隐藏同名的 plain 变量。

循环变量

只能存在于指令的嵌套内容,由指令(如 list)自动创建;宏的参数是局部变量,而不是循环变量。

循环变量会隐藏同名的局部变量和 plain 变量,且内部循环变量会隐藏外部循环变量。

如果想要跳出作用域,直接使用数据模型中的变量,使用 globals 关键字即可:

<#assign user = "Joe">${user}${.globals.user}
输出:
JoeDoe

6. 运算符的优先级

FreeMarker 中的运算符优先级如下 (由高到低排列):

  1. 一元运算符:!
  2. 内建函数:?
  3. 乘除法: *, / , %
  4. 加减法:- , +
  5. 比较: > , < , >= , <=
  6. 相等: == , = , !=
  7. 逻辑与:&&
  8. 逻辑或: ||
  9. 数字范围:..

7. include 指令

include 指令的作用类似于 JSP 的包含指令,用于包含指定页。include 指令的语法格式如下:
<#include filename [options]>
示例:
<#include "/header.html"><#include "/footer.ftl">

8. import 指令及命名空间

import指令类似于 java 里的 import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件。

我们通过 assign 和 macro 创建的变量的集合就是命名空间。命名空间的作用在各种编程语言中都已见识,这里假设我们定义了自己的 macro 和 assign 位于 lib/mylib.ftl 中。

<#macro copyright date><p>Copyright (C) ${date} Julia Smith. All rights reserved.</p></#macro><#assign mail = "jsmith@acme.com">
通过 import 来引用:
<#import "/libs/mylib.ftl" as my> <#-- 被称为"my"的哈希表就会是那个"大门" --> <@my.copyright date="1999-2002"/>${my.mail}
输出:
<p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>jsmith@acme.com

覆盖变量

引入命名空间后替换变量需要在 assign 的基础上加上 in 关键字:

<#import "/lib/mylib.ftl" as my>${my.mail}<#assign mail = "jsmith@other.com" in my>${my.mail}
输出:
jsmith@acme.comjsmith@other.com

9. noparse 指令

noparse 指令指定 FreeMarker 不处理该指定里包含的内容,该指令的语法格
<#noparse>...</#noparse>
示例:
<#noparse><#list animals as animal>    <li>${animal.name} for ${animal.price} Euros</li></#list></#noparse>
输出:
<#list animals as animal>    <li>${animal.name} for ${animal.price} Euros</li></#list>

10. setting 指令

该指令用于设置 FreeMarker 的运行环境, 该指令的语法格式如下:
<#setting name=value>

在这个格式中,name的取值范围包含如下几个:

  • locale: 该选项指定该模板所用的国家 / 语言选项
  • number_format: 指定格式化输出数字的格式
  • boolean_format: 指定两个布尔值的语法格式, 默认值是 true,false
  • date_format,time_format,datetime_format: 指定格式化输出日期的格式
  • time_zone: 设置格式化输出日期时所使用的时区

三、内建函数

内建函数以?形式提供变量的不同形式或者其他信息。多个内建函数可以通过?连接来使用。

字符串常用的内建函数:

  • html: 将字符串中的所有特殊 HTML 字符进行转义。例如:<替换成&lt;
  • cap_first: 将字符串的首字母大写
  • lower_case: 将字符串转换为小写形式
  • upper_case: 将字符串转换为大写形式
  • trim: 去掉字符串首尾的空格

集合常用的内建函数:

  • size: 集合中元素的个数
  • chunk(size): 分成几个一组

数字常用的内建函数:

  • int: 将数字转换为整数。方式为直接去除小数部分。

示例:

${test?html}${test?lower_case?html}

${3 * 2 + 2}                <#-- 8 -->${3 * (2 + 2)}              <#-- 12 -->${3 * ((2 + 2) * (1 / 2))}  <#-- 6 -->${"green " + "mouse"?upper_case}    <#-- green MOUSE -->${("green " + "mouse")?upper_case}  <#-- GREEN MOUSE -->

number_to_datetime

一个转换时间格式的内建函数。

${timestamp?number_to_datetime?string["MM-dd HH:mm"]}

eval

将字符串作为 ftl 模板输出,就和 javascript 原生的 eval 有点类似。

示例:

${(col['name']?eval)}

四、宏

宏是在模板中使用 macro 指令定义,宏是和某个变量关联的模板片断,以便在模板中通过用户定义指令使用该变量,有人说用 freemarker,但没有用到它的宏(macro),就等于没有真正用过 freemarker。说的就是宏是 freemarker 的一大特色。

其基本语法如下:

<#macro name param1 param2 ... paramN>    ...    <#nested loopvar1, loopvar2, ..., loopvarN>    ...    <#return>    ...</#macro>

在上面的格式片段中, 包含了如下几个部分:

  • name:name属性指定的是该自定义指令的名字, 使用自定义指令时可以传入多个参数
  • paramX: 该属性就是指定使用自定义指令时报参数, 使用该自定义指令时, 必须为这些参数传入值
  • nested指令:nested标签输出使用自定义指令时的中间部分
  • nested指令中的循环变量: 这此循环变量将由macro定义部分指定, 传给使用标签的模板
  • return指令: 该指令可用于随时结束该自定义指令.

需要注意的是:调用宏时,与使用 FreeMarker 的其他指令类似,只是使用@替代 FTL 标记中的#

1. macro 定义模板,然后调用直接显示
<#macro greet><font size="+2">Hello World!</font></#macro>

使用:
<@greet/><@greet></@greet>
输出:
<font size="+2">Hello World!</font><font size="+2">Hello World!</font>
2. 在 macro 指令中可以在宏变量之后定义参数
<#macro greet person><font size="+2">Hello ${person}!</font></#macro>

使用:
<@greet person="Doe"></@greet><@greet person="Joe"/><@greet "Foo"/>

输出:
<font size="+2">Hello Doe!</font><font size="+2">Hello Joe!</font><font size="+2">Hello Foo!</font>

3. macro 定义多个参数
macro 可以有多个参数,参数的次序是无关的(如果使用 Postional style 调用则需要按顺序),在 macro 指令中只能使用定义的参数,并且必须对所有参数赋值,可以在定义参数时指定缺省值:
<#macro greet person color="black"><font size="+2" color="${color}">Hello ${person}!</font></#macro>

使用:

<#-- Named style --><@greet person="Doe"></@greet><@greet person="Doe"/><@greet person="Doe" color= "green"/><#-- Postional style --><@greet "Doe"/><@greet "Doe", "green"/>

输出:

<font size="+2" color="black">Hello Doe!</font><font size="+2" color="black">Hello Doe!</font><font size="+2" color="green">Hello Doe!</font><font size="+2" color="black">Hello Doe!</font><font size="+2" color="green">Hello Doe!</font>
4. 自定义指令嵌套内容 <#nested>
<#macro border><table border=4 cellspacing=0 cellpadding=4>    <tr>        <td>            <#nested>        </td>    </tr></table></#macro>

使用:
<@border>The bordered text</@border>

输出:

<table border=4 cellspacing=0 cellpadding=4>    <tr>        <td>            The bordered text        </td>    </tr></table>

<#nested>就相当于占位符

<#nested>指令可以被多次调用:

<#macro do_thrice>    <#nested>    <#nested>    <#nested></#macro>

使用:

<@do_thrice>Anything.</@do_thrice>

输出:

Anything.Anything.Anything.
5. 局部变量对嵌套内容不可见
<#macro repeat count>    <#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: ? ? ?

其中嵌套内容中的y,x,count都是没定义的,所以取不到值。

6. 宏定义中使用循环变量
nested 指令也可以有循环变量(循环变量的含义见下节),调用宏的时候在宏指令的参数后面,分号隔开依次列出循环变量的名字,格式如下:
<@macro_name paramter list; loop variable 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>

count是宏的参数,c,halfc,last则为循环变量,与宏中<#nested>里定义的x,x/2,x==count一一对应。

输出:

1, 0.52, 13, 1.54, 2 Last!

引用
循环变量和宏标记指定的不同不会有问题,如果调用时少指定了循环变量,那么多余的值不可见。调用时多指定了循环变量,多余的循环变量不会被创建:

<@repeat count=4 ; c, halfc, last>${c}. ${halfc}<#if last> Last!</#if></@repeat><@repeat count=4 ; c, halfc>${c}. ${halfc}</@repeat><@repeat count=4>Just repeat it...</@repeat>

转载自:https://hran.me/archives/freemarker.html

原创粉丝点击