您的位置:首页 > 其它

FreeMarker基础入门知识4 -自定义指令

2017-03-21 14:18 585 查看


1:自定义指令

Page Contents
基本内容
参数
嵌套内容
宏和循环变量
自定义指令和宏进阶

自定义指令可以使用 
macro
 指令来定义, 这是模板设计者所关心的内容。 Java程序员若不想在模板中实现定义指令,而是在Java语言中实现指令的定义, 这时可以使用 
freemarker.template.TemplateDirectiveModel
 类来扩展
(请参考 后续章节)。


基本内容

宏是有一个变量名的模板片段。可以在模板中使用宏作为自定义指令, 这样就能进行重复性的工作。例如,创建一个宏变量来输出大字号的''Hello Joe!'':

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


macro
 指令自身不输出任何内容, 它只是用来创建宏变量,所以就会有一个名为 
greet
 的变量。在 
<#macro greet>
 和 
</#macro>
 之间的内容 (称为 宏定义体)
将会在使用该变量作为指令时执行。可以在FTL标记中通过 
@
代替
#
来使用自定义指令。 使用变量名作为指令名。而且,自定义指令的 结束标记 也是需要的。那么,
就可以这样来使用 
greet


<@greet></@greet>


因为 
<anything></anything>
 和 
<anything/>
 是相同的,
也可以使用单标记形式(如果你了解 XML,那么就应该很熟悉了):

<@greet/>


将会输出:

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


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

Note:
程序员通常将使用 
<@...>
 这称为 宏 调用


参数

我们来改进 
greet
 宏使之可以使用任意的名字, 而不仅仅是''Joe''。为了实现这个目的,就要使用到 参数。在 
macro
 指令中,宏名称的后面位置是用来定义参数的。这里我们仅在 
greet
 宏中定义一个参数,
person


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


那么我们就看到了,宏参数的真实值是可以作为变量 (
person
)放在宏定义体中的。使用 预定义指令时,参数的值
(
=
号后边的值)可以是 FTL 表达式。 那么,不像HTML,
"Fred"
 和 
"Batman"
 引号就可以不用要了。 
<@greet
person=Fred/>
 也意味着使用变量的值 
Fred
 作为
person
 参数, 而不是字符串
"Fred"
。当然参数值并不一定是字符串类型, 也可以是数字,布尔值,哈希表,序列等。也可以在 
=
 号左边使用复杂表达式(比如 
someParam=(price
+ 50)*1.25
)。

自定义指令可以有多个参数。如下所示,再添加一个新的参数 
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
 指令中定义的参数(本例中是:
person
 和 
color
)。 那么当你尝试 
<@greet person="Fred" color="black"
background="green"/>
 的时候就会发生错误,因为并没有在 
<#macro...>
 中提及参数 
background


同时也必须给出在宏中定义所有参数的值。如果尝试 
<@greet person="Fred"/>
 时也会发生错误, 因为忘记指定 
color
的值了。 很多情况下需要给一个参数指定一个相同的值,所以我们仅仅想在这个值发生变化后重新赋给变量。 那么要达到这个目的,在
macro
指令中必须这么来指定变量: 
param_name=usual_value

例如,当没有特定值的时候,我们想要给 
color
 赋值为 
"black"
,那么 
greet
 指令就要这么来写:

<#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"

那么就写成: 
<@greet person="Fred" color="red"/>
, 这时
macro
 指令就会使用这个值来覆盖之前设置的通用值, 参数 
color
 的值就会是 
"red"
 了。

根据已知的 FTL 表达式规则, 明白下面这一点是至关重要的。
someParam=foo
 和 
someParam="${foo}"
 是不同的。第一种情况,
是把变量 
foo
 的值作为参数的值来使用。第二种情况则是使用 插值形式的字符串
那么参数值就是字符串了,这个时候, 
foo
 的值呈现为文本, 而不管 
foo
 是什么类型的(数字,日期等)。看下面这个例子: 
someParam=3/4
 和 
someParam="${3/4}"
 是不同的。
如果指令需要
someParam
 是一个数字值, 那么就不要用第二种方式。切记不要改变这些。

宏参数的另外一个重要的方面是它们是局部变量。 更多局部变量的信息可以阅读:在模板中定义变量


嵌套内容

自定义指令可以嵌套内容,和预定义指令相似:
<#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
</td></tr></table>


nested
 指令也可以多次被调用,例如:

<#macro do_thrice>
<#nested>
<#nested>
<#nested>
</#macro>
<@do_thrice>
Anything.
</@do_thrice>


将会输出:

Anything.
Anything.
Anything.


如果不使用 
nested
 指令, 那么嵌套的内容就不会被执行,如果不小心将 
greet
 指令写成了这样:

<@greet person="Joe">
Anything.
</@greet>


FreeMarker 不会把它视为错误,只是输出:

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


嵌套的内容被忽略了,因为 
greet
 宏没有使用 
nested
 指令。

嵌套的内容可以是任意有效的FTL,包含其他的用户自定义指令,这样也是对的:

<@border>
<ul>
<@do_thrice>
<li><@greet person="Joe"/>
</@do_thrice>
</ul>
</@border>


将会输出:

<table border=4 cellspacing=0 cellpadding=4><tr><td>
<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 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: ? ? ?


因为 
y
, 
x
 和 
count
 是宏的局部(私有)变量,从宏外部定义是不可见的。 此外不同的局部变量的设置是为每个宏自己调用的,所以不会导致混乱:

<#macro test foo>${foo} (<#nested>) ${foo}</#macro>
<@test foo="A"><@test foo="B"><@test foo="C"/></@test></@test>


将会输出:

A (B (C () C) B) A



宏和循环变量

像 
list
这样的预定义指令可以使用循环变量; 可以阅读 在模板中定义变量 来理解循环变量。

自定义指令也可以有循环变量。比如我们来扩展先前例子中的 
do_thrice
 指令,就可以拿到当前的循环变量的值。 而对于预定义指令(如
list
),当调用指令时,循环变量的 name是给定的(比如 
<#list foos as foo>...</#list>
 中的 
foo
),变量 value 的设置是由指令本身完成的。

<#macro do_thrice>
<#nested 1>
<#nested 2>
<#nested 3>
</#macro>
<@do_thrice ; x> <#-- user-defined directive uses ";" instead of "as" -->
${x} Anything.
</@do_thrice>


将会输出:

1 Anything.
2 Anything.
3 Anything.


语法规则是给确定"循环"的循环变量传递真实值(比如重复嵌套内容)来作为 
nested
 指令的参数(当然参数可以是任意的表达式)。 循环变量的名称是在自定义指令的开始标记(
<@...>
) 的参数后面通过分号确定的。

一个宏可以使用多个循环变量(变量的顺序是很重要的):

<#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!


在自定义指令的开始标签(分号之后)为循环变量指定不同的数字是没有问题的, 而不能在 
nested
 指令上使用。如果在分号之后指定的循环变量少, 那么就看不到 
nested
指令提供的最后的值, 因为没有循环变量来存储这些值,下面的这些都是可以的:

<@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>


如果在分号后面指定了比 
nested
 指令还多的变量, 那么最后的循环变量将不会被创建(在嵌套内容中不会被定义)。


自定义指令和宏进阶

现在你也许已经阅读过 FreeMarker 参考手册的相关部分了:
调用自定义指令
macro
 指令


也可以在FTL中定义方法,参见 
function
 指令


也许你对命名空间感兴趣: 命名空间。 命名空间可以帮助你组织和重用经常使用的宏。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: