1. 宏
#macro 脚本元素允许模板设计者在VTL 模板中定义重复的段。 Velocimacros 不管是在复杂还是简单的场合都非常有用。下面这个Velocimacro,仅用来节省击键和减少排版错误,介绍了一些Velocity宏的概念。
#macro( d )
<tr><td></td></tr>
#end
在例子中,Velocimacro定义为d,它可以象调用其他VTL 指令一样的形式来进行调用:
#d()
当这个模板被调用时, Velocity 将 #d() 替换为一个单行的空表格。
Velocimacro 可以带一些参数,也可以不带参数(如上例所示)。但在他被调用时,所带的参数必须和其定义时的参数一样。很多Velocimacros 定义为不止一个参数。下面这个宏带有两个参数,一个颜色,一个数组。
#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
<tr><td bgcolor=$color>$something</td></tr>
#end
#end
在这个例子中定义的Velocimacro,名为tablerows, 要求两个参数。 第一个参数代替$color, 第二个代替$somelist。
可以写进VTL 模板中的东西都可以写进Velocimacro 的主体部分。tablerows 宏其实是一个foreach 语句。在#tablerows 宏的定义中有两个#ende 语句,第一个属于#foreach, 第二个结束宏定义。
#set( $greatlakes = [“Superior”,”Michigan”,”Huron”,”Erie”,”Ontario”] )
#set( $color = “blue” )
<table>
#tablerows( $color $greatlakes )
</table>
请注意$greatlakes 替换了$somelist。 这样,当#tablerows 宏被调用时,将产生以下输出:
<table>
<tr><td bgcolor=”blue”>Superior</td></tr>
<tr><td bgcolor=”blue”>Michigan</td></tr>
<tr><td bgcolor=”blue”>Huron</td></tr>
<tr><td bgcolor=”blue”>Erie</td></tr>
<tr><td bgcolor=”blue”>Ontario</td></tr>
</table>
Velocimacros 在Velocity 模板语句内定义,这意味着它在同一站点内的其他Velocity 模板中并不有效。定义一个宏,并使其与其他模板共享很具有明显的优点:他减少了在大量的模板内重复定义宏的工作,并减少了出错的机会,并确保对其他宏的改 变对其他所有模板有效。
但如果 #tablerows($color $list) 宏是在一个Velocimacros 模板库内定义的,它就可以被其他常规模板所用。当然,它可以用于各种目的,也可重用多次。在表示所有真菌类(fungi)的mushroom.vm 模板中,#tablerows 宏可以被用来列出典型的蘑菇。
#set( $parts = [“volva”,”stipe”,”annulus”,”gills”,”pileus”] )
#set( $cellbgcol = “#CC00FF” )
<table>
#tablerows( $cellbgcol $parts )
</table>
我们对mushroom.vm执行请求,Velocity 将在模板库内找到#tablerows 宏 (在velocity.properties 文件中定义)并产生以下输出:
<table>
<tr><td bgcolor=”#CC00FF”>volva</td></tr>
<tr><td bgcolor=”#CC00FF”>stipe</td></tr>
<tr><td bgcolor=”#CC00FF”>annulus</td></tr>
<tr><td bgcolor=”#CC00FF”>gills</td></tr>
<tr><td bgcolor=”#CC00FF”>pileus</td></tr>
</table>
Velocimacro 参数
Velocimacros 的参数可以是以下的VTL元素:
引用(Reference): 以 ‘$’ 打头的元素
字面字符串(String literal) : 比如”$foo” 或 ‘hello’
字面数字: 1, 2 ….
整数范围: [ 1..2] 或 [$foo .. $bar]
对象数组: [ “a”, “b”, “c”]
布尔真
布尔假
当把引用作为参数传递给Velocimacros时,请注意引用是按“名字”传递 的。这意味着他们的值在每次使用他们的Velocimacro中产生。这个特性允许你在方法调用是传递引用,并在每次使用时进行方法调用。例如,Fo, 当调用下面的Velocimacro 时,
#macro( callme $a )
$a $a $a
#end
#callme( $foo.bar() )
结果是,在方法bar() 中,引用 $foo 被调用了3次。
咋看时,这个特征让人吃惊,当当你考虑一下Velocimacros的原本动机 – 在VTL模板中避免很多“剪切复制”操作—你就会明白。它允许你将无状态对象,比如在一个颜色表格行内重复产生一些颜色次序的对象,传递给 Velocimacro。
如果你需要使用这个特征,你通常可以从方法内取得一个值,作为一个新的引用传递给 宏:
#set( $myval = $foo.bar() )
#callme( $myval )
Velocimacro 属性
在velocity.properties 文件中有数行定义可以用来灵活实现Velocimacros。详细情况请参见开发指南(Developer Guide)。
velocimacro.library – 是一个逗号分隔的所有Velocimacro 模板库的列表。默认情况下, Velocity 搜寻一个单一的库VM_global_library.vm.。 预先配置的模板路径用来查找Velocimacro 库。
velocimacro.permissions.allow.inline – 这个属性决定Velocimacros 是否可以在常规模板内定义,取值为逻辑True或者False。默认情况下,设置为true,允许设计者在产规模板内定义宏。
velocimacro.permissions.allow.inline.to.replace.global – 逻辑true 或者false,允许标明是否允许在常规模板内定义的Velocimacro 代替在模板库中定义并通过velocimacro.library属 性在启动时装入的全局宏。默认设置为false。
velocimacro.permissions.allow.inline.local.scope – 逻辑true 或者false,默认值为false。 控制是否 在模板内定义的Velocimacros 仅在定义它的模板内可见。换句话说,如果设置为true,一个模板可以定义仅能被他所用的宏。你可以用它来做一些漂亮的宏,如果一个全局调用另一个全局 宏,在局部(inline)范围内,当被一个模板调用时,该模板可以定义一个被第一个全局宏调用的第二个全局宏的私有实现。其他所有模板都不受影响。
velocimacro.context.localscope – 逻辑值true 或者 false,缺省值为false。但设置为true时,所有在Velocimacro 内通过 #set() 进行的修改都将被视为Velocimacro 的本地行为,不会影响到其上下文。
velocimacro.library.autoreload – 此属性控制Velocimacro 库的自动载入。缺省值为false。如果设置为true,被调用的Velocimacro得源库将被检查是否改变,并在必要是重新载入。这将使你可以改变 和测试Velocimacro 库,而不必重新启动应用服务器或者servlet容器,就象你工作在常规模板一样。这个模时仅在资源载入器的缓存模时被关闭的情况下有效 (如 file.resource.loader.cache = false )。此特征为开发时设计,不要在生产模式时使用。
Velocimacro Trivia
当前, Velocimacros 在其首次在模版中使用前必须首先定义它。这意味着, #macro() 宣称应该在使用Velocimacros之前。
如果你想#parse() 一个包含#macro() 指令的模板,记住这个非常重要。因为#parse() 在运行时发生,解析器在解析时要决定是否模版中一个看起来像VM的元素真是VM,所以解析一系列VM 宣称可能并不能如愿地工作的很好。为避免如此,可以简单地使用velocimacro.library 的办法,使Velocity 在启动时载入VM。
2. 转义 VTL 指令
VTL 可以通过反斜杠(“\”)来进行转义,directives can be escaped with the backslash character in a manner similar to valid VTL references.
## #include( “a.txt” ) renders as <contents of a.txt>
#include( “a.txt” )
## \#include( “a.txt” ) renders as \#include( “a.txt” )
\#include( “a.txt” )
## \\#include ( “a.txt” ) renders as \<contents of a.txt>
\\#include ( “a.txt” )
在转义在一个单一指令内包含多个脚本元素(比如f-else-end语句)的指令时 应多加小心。下面是一个典型的VTL if语句;
#if( $jazz )
Vyacheslav Ganelin
#end
如果 $jazz为 true,输出是
Vyacheslav Ganelin
如果 $jazz 为false,将没有输出。转义脚本元素将改变输出。考虑下面的情况;
\#if( $jazz )
Vyacheslav Ganelin
\#end
不管 $jazz 是真或假,输出都是
#if($ jazz )
Vyacheslav Ganelin
#end
事实上,因为所有脚本元素都被转义了, $jazz 永远不会被求值。将设反斜杠在被合法转义的脚本元素之前
\\#if( $jazz )
Vyacheslav Ganelin
\\#end
这时,如果$jazz 为真,输出是
\ Vyacheslav Ganelin
\
为理解这个情况,请注意在一个新行结束是将在输出中忽略新的一行。因此,经 过#if()前的’\\’ 加工后,#if()块紧跟第一个’\’。最后一个\位于新的一行,因为在’Ganelin’后又一个新行,所以,最后的那个位于#end 之前的\\是语句块的一部分。
如果 $jazz 为false,这里将没有输出。注意,在开始破坏了if语句的情况将不能被正确转义:
\\\#if( $jazz )
Vyacheslave Ganelin
\\#end
这里,#if 被转义,但有一个#end 被保留了;所以有多个结束语句将导致解析错误。