Velocity用户手册是帮助页面设计者和内容提供者认识Velocity和其简单而功能强大的脚本语言――Velocity模板语言(VTL)。在手册上的许多例子,都是用Velocity插入动态的内容到网页上,但是所有的VLT例子都能应用到其他的页面和模板中。
感谢使用 Velocity!
Velocity是什么?
Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。
当Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocity将java代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在JSP和PHP之外又提供了一种可选的方案。
Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQL和PostScript、XML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务(template service)。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。
Velocity能为我们作什么?
Mud 商店例子
假设你是一家专门出售Mud的在线商店的页面设计人员,让我们暂且称它为“在线MUD商店”。你们的业务很旺,客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站,登陆后就允许他们查看订单并购买更多的mud。现在,一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud,由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的,所以某天有一个问题可能会冒出来:为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢?
Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员,你希望每个用户登陆时都拥有自己的页面。
你会见了一些公司内的软件工程师,你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边,看一看你应该作些什么吧。
你可能在页面内嵌套如下的VTL声明:
$flogger.getPromo( $mud ) |
Hello Velocity World!
一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。
注释
注释是那些描述文本不出现在模板引擎输出里面。注释一个主要用处是提醒自己和解释出现在VTL中的声明,或是其他用途。下面是一个在VLT中的注释例子。
This is a single line comment.
单行注释以##开始,结束在这行的结尾。如果你要写几行注释,这没有必要写几个单行注释。
多行注释,以#开始#结束,可以解决这个问题。
This is text that is outside the multi-line comment.
Online visitors can see it.
#*
Thus begins a multi-line comment. Online visitors won’t
see this text because the Velocity Templating Engine will
ignore it.
*#
Here is text outside the multi-line comment; it is visible.
这里有几个例子关于单行和多行注释如何工作。
This text is visible. ## This text is not.
This text is visible.
This text is visible. #* This text, as part of a multi-line comment,
is not visible. This text is not visible; it is also part of the
multi-line comment. This text still not visible. *# This text is outside
the comment, so it is visible.
This text is not visible.
这里有第三种类型的注释,VLT注释块,可能用于存放文档的作者名称和版本信息。
#**
This is a VTL comment block and
may be used to store such information
as the document author and versioning
information:
@author
@version 5
*#
References
在VTL中有三种类型的references:变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者,你和你的工程师必须就references的名称达成共识,以便你可以在你的template中使用它们。
所有的 reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象,那么Velocity将调用它的toString()方法将这个对象转型为String类型。
变量
非正式变量是由“$”开头,接着是VTL标识符。VLT标识符必须以字母(a…z,A…Z)开头。剩下的部分限于以下几种:
字母(a…z,A…Z)
数字(0…9)
连字符(“-”)
下划线(“_”)
这里是几个在VTL中有效的变量reference。
$foo
$mudSlinger
$mud-slinger
$mud_slinger
m
u
d
S
l
i
n
g
e
r
1
当
V
L
T
定
义
一
个
变
量
,
例
如
mudSlinger1 当VLT定义一个变量,例如
mudSlinger1当VLT定义一个变量,例如foo,变量能或者通过模板中的set 方法,或者通过Java 代码获得值。
例如,Java 变量 $foo 的值是 bar ,在这个模板被请求时,在网页上 bar 会替代所有 $foo 。选择地,假如包括了下面的声明:
#set( $foo = “bar” )
按照这样的设置,输出就会跟之前的一样。
属性
第二种有趣的VTL reference就是属性,而且属性有一种与众不同的格式。非正式变量是由“$”开头,接着是VTL标识符,再接着就是字符(“.”)和其他的VLT标识符。这里是一些在VLT中有效属性的定义:
$customer.Address
p
u
r
c
h
a
s
e
.
T
o
t
a
l
在
第
一
个
例
子
中
purchase.Total 在第一个例子中
purchase.Total在第一个例子中customer.Address有两种含义。它可以表示:查找hashtable对象customer中以Address为关键字的值;也可以表示调用customer对象的getAddress()方法。当你的页面被请求时,Velocity将确定以上两种方式选用那种,然后返回适当的值。
方法
一个方法就是被定义在java中的一段代码,并且它有完成某些有用工作的能力,例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References,一般还包括一个VTL方法体。一个VTL方法体包括一个VLT标识接着一个左括号(“(”),接着是参数列表,再接着是右括号(“)”)。这里是一些在VTL中有效的方法定义:
$customer.getAddress()
$purchase.getTotal()
$page.setTitle( “My Home Page” )
p
e
r
s
o
n
.
s
e
t
A
t
t
r
i
b
u
t
e
s
(
[
"
S
t
r
a
n
g
e
"
,
"
W
e
i
r
d
"
,
"
E
x
c
i
t
e
d
"
]
)
前
两
个
例
子
person.setAttributes( ["Strange", "Weird", "Excited"] ) 前两个例子
person.setAttributes(["Strange","Weird","Excited"])前两个例子customer.getAddress()和
p
u
r
c
h
a
s
e
.
g
e
t
T
o
t
a
l
(
)
看
起
来
挺
想
上
面
的
属
性
purchase.getTotal()看起来挺想上面的属性
purchase.getTotal()看起来挺想上面的属性customer.Address 和
p
u
r
c
h
a
s
e
.
T
o
t
a
l
。
如
果
你
觉
得
他
们
之
间
有
某
种
联
系
的
话
,
那
你
是
正
确
的
。
V
T
L
属
性
可
以
作
为
V
T
L
方
法
的
缩
写
。
purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。 VTL属性可以作为VTL方法的缩写。
purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。VTL属性可以作为VTL方法的缩写。customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。
非正式定义能够用下面的方法:
$sun.getPlanets()
$annelid.getDirt()
$album.getPhoto()
我们期待那些方法返回属于太阳系的行星的名称,喂养我们的蚯蚓,或者从相册里面取出一张照片。只有长符号为下面的方法服务。
$sun.getPlanet( [“Earth”, “Mars”, “Neptune”] )
Can’t pass a parameter list with $sun.Planets
$sisyphus.pushRock()
Velocity assumes I mean $sisyphus.getRock()
$book.setTitle( “Homage to Catalonia” )
Can’t pass a parameter list
正式reference标记
非正式references用于上述的例子中。但是同样有正式的references,如下面所示:
${mudSlinger}
${customer.Address}
p
u
r
c
h
a
s
e
.
g
e
t
T
o
t
a
l
(
)
在
几
乎
所
有
场
合
你
都
可
以
使
用
非
正
式
r
e
f
e
r
e
n
c
e
s
,
但
是
在
某
些
场
合
,
只
能
使
用
正
式
r
e
f
e
r
e
n
c
e
才
能
正
确
处
理
。
设
想
你
创
建
一
个
句
子
:
{purchase.getTotal()} 在几乎所有场合你都可以使用非正式references,但是在某些场合,只能使用正式reference 才能正确处理。 设想你创建一个句子:
purchase.getTotal()在几乎所有场合你都可以使用非正式references,但是在某些场合,只能使用正式reference才能正确处理。设想你创建一个句子:vice 作为句子的名词 。目标是为了使某些人选择不同的词,产生下面两种结果之一:“Jack is a pyromaniac.” 或者 “Jack is a kleptomaniac.”。使用非正式定义不太适合用于这种情况。看一下下面的例子:
Jack is a
v
i
c
e
m
a
n
i
a
c
.
本
来
变
量
是
vicemaniac. 本来变量是
vicemaniac.本来变量是vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写
Jack is a
v
i
c
e
m
a
n
i
a
c
.
现
在
V
e
l
o
c
i
t
y
就
知
道
r
e
f
e
r
e
n
c
e
是
{vice}maniac. 现在Velocity就知道reference 是
vicemaniac.现在Velocity就知道reference是vice,而不是$vicemaniac。正式定义经常用于模板中refernces与文本连接在一起的情况。
Quiet reference notation
当Velocity遇到没有定义的reference,通常它会直接输出reference。例如:假如下面的reference出现在一个VTL模板中:
当 form 最初加载的时候,变量
e
m
a
i
l
没
有
值
,
但
你
想
出
现
一
个
空
白
的
文
本
框
设
定
值
为
“
email 没有值,但你想出现一个空白的文本框设定值为“
email没有值,但你想出现一个空白的文本框设定值为“email”。
使用quiet reference notation可以使Velocity正常显示,你需要用
!
e
m
a
i
l
,
代
替
!email ,代替
!email,代替email。所以上面的例子,会改成下面:
这样文本框的初始值就不会是email而是空值了。
正式和quiet格式的reference notation也可一同使用,像下面这样:
Getting literal
Velocity使用特殊字符KaTeX parse error: Expected 'EOF', got '#' at position 2: 和#̲来帮助它工作,所以如果要在te…字符。
货币字符
这是没有问题的:“I bought a 4 lb. sack of potatoes at the farmer’s market for only $2.50!”,VTL中使用$2.5这样的货币标识是没有问题得的,VTL不会将它错认为是一个reference,因为VTL中的reference总是以一个大写或者小写的字母开始。
Escaping valid VTL reference
某些情况使用Velocity可能会觉得很烦恼。逃避特殊符是处理出现在你的模板中VTL特殊符有效方法,就是使用反斜杠(“\”)。
#set( $email = “foo” )
$email
假如Velocity在你的模板中遇到 $email ,它会搜索上下文,得到相应的值。这里的输出是 foo ,因为 $email 被定义了。假如 $email 没有被定义,输出会是 $email 。
设想 $email 被定义了(例如,它的值是 foo ),而且你想输出 $email 。这里有几种方法能达到目的,但是最简单的是使用逃避符。
The following line defines $email in this template:
#set( $email = “foo” )
$email
$email
\$email
\$email
将显示为:
foo
$email
\foo
$email
注意到 “\” 屏蔽了左边的 “$”。屏蔽左边规则,使得 \$email 显示为 \$email 。那些例子与 $email没有定义相比较。
$email
$email
\$email
\$email
将显示为:
$email
$email
\$email
\$email
注意Velocity处理 定义了的references 与没有定义的不一样。这里 set $foo 的值为 gibbous。
#set( $foo = “gibbous” )
$moon =
f
o
o
输
出
会
是
:
foo 输出会是:
foo输出会是:moon = gibbous ,$moon 按照字面上输出因为它没有定义,gibbous 替代 $foo 输出。
避开VTL的 directives 还有其他方法,在 Directives 那章节会更详细描述。
Case substitution
现在你已经对reference比较熟悉了,你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如:
$foo
$foo.getBar()
is the same as
$foo.Bar
$data.getUser(“jon”)
is the same as
$data.User(“jon”)
$data.getRequest().getServerName()
is the same as
$data.Request.ServerName
is the same as
${data.Request.ServerName}
那些例子说明了同样的references 用法。Velocity 利用Java的 instrospection 和 bean features解决 reference 的名称对于对象和对象方法的问题。它可以出入你的模板中和求出 references 的值。
Velocity 是模仿Sun微系统中的Bean规范定义的,因而它是很灵活的。然而,它的开发者已经很努力地捕捉和纠正可能出现的错误。当方法 getFoo() 在模板中 被 $bar.foo调用时,Velocity首先尝试 $getfoo。如果失败,它会继续尝试 $getFoo。同样地,当模板查询 $bar.Foo,Velocity会先尝试 $getFoo(),然后再尝试
g
e
t
f
o
o
(
)
。
但
是
,
注
意
V
T
L
中
不
会
将
r
e
f
e
r
e
n
c
e
解
释
为
对
象
的
实
例
变
量
。
例
如
:
getfoo()。 但是,注意VTL中不会将reference解释为对象的实例变量。例如:
getfoo()。但是,注意VTL中不会将reference解释为对象的实例变量。例如:foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。
Directives
Reference允许设计者使用动态的内容,而directive使得你可以应用java代码来控制你的显示逻辑,从而达到你所期望的显示效果。
#set
#set标志是用于对一个reference赋值。值会赋给一个变量或者一个属性,而且赋值会在括号里出现:
#set( $primate = “monkey” )
#set( $customer.Behavior = $primate )
左边(LHS) 一定是一个变量或者一个属性。右边(RHS)可以是下面中的一个类型:
变量
字符串
属性
方法
数字
数组
这些例子显示上述的每一种类型:
#set( $monkey = $bill ) ## variable reference
#set( $monkey.Friend = “monica” ) ## string literal
#set( $monkey.Blame = $whitehouse.Leak ) ## property reference
#set( $monkey.Plan =
s
p
i
n
d
o
c
t
o
r
.
w
e
a
v
e
(
spindoctor.weave(
spindoctor.weave(web) ) ## method reference
#set( $monkey.Number = 123 ) ##number literal
#set( $monkey.Say = [“Not”, KaTeX parse error: Expected 'EOF', got '#' at position 16: my, "fault"] ) #̲# ArrayList 注意:…monkey.Say.get(0)。
RHS也可以是一个简单的算术表达式:
#set( $value = $foo + 1 )
#set( $value = $bar - 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )
如果你的RHS是一个null,VTL的处理将比较特殊:它将指向一个已经存在的reference,这对初学者来讲可能是比较费解的。例如:
#set( $result = $query.criteria(“name”) )
The result of the first query is $result
#set( $result = $query.criteria(“address”) )
The result of the second query is
r
e
s
u
l
t
如
果
result 如果
result如果query.criteria(“name”)返回一个“bill”,而$query.criteria(“address”)返回的是null,则显示的结果如下:
The result of the first query is bill
The result of the second query is bill
这容易使新手糊涂:创建一个#foreach循环,企图想通过一个属性或者一个方法 #set一个reference,然后马上就用 #if 测试。例如:
#set( $criteria = [“name”, “address”] )
#foreach( $criterion in $criteria )
#set( $result = $query.criteria($criterion) )
#if( $result )
Query was successful
#end
#end
在上面的例子中,程序将不能智能的根据
r
e
s
u
l
t
的
值
决
定
查
询
是
否
成
功
。
在
result的值决定查询是否成功。在
result的值决定查询是否成功。在result被#set后(added to the context),它不能被设置回null(removed from the context)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。
为了解决以上问题我们可以通过预先定义的方式:
#set( $criteria = [“name”, “address”] )
#foreach( $criterion in $criteria )
#set( $result = false )
#set( $result = $query.criteria($criterion) )
#if( $result )
Query was successful
#end
#end
不像其他Velocity指示符号,#set 没有一个#end 结束。
String Literals
当你使用#set directive,String literal封闭在一对双引号内。象下面:
#set( $directoryRoot = “www” )
#set( $templateName = “index.vm” )
#set(
t
e
m
p
l
a
t
e
=
"
template = "
template="directoryRoot/$templateName" )
$template
上面这段代码的输出结果为:
www/index.vm
但是,当string literal被封装在单引号内时,它将不被解析:
#set( $foo = “bar” )
$foo
#set(
b
l
a
r
g
h
=
′
blargh = '
blargh=′foo’ )
$blargh
输出为:
bar $foo
上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。
条件语句
If / ElseIf / Else
当一个web页面被生成时使用Velocity的#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:
#if( KaTeX parse error: Expected 'EOF', got '#' at position 37: …city!</strong> #̲end 上例中的条件语句将在以…foo是一个boolean型的变量,且它的值为true;(ii)$foo变量的值不为null。这里需要注意一点:Velocity context仅仅能够包含对象,所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值,Velocity也会利用内省机制将它转换为一个Boolean的相同值。
如果条件成立,那么#if和#end之间的内容将被显示。在这个例子中,如果 $foo的值为true,输出为“Velocity!”。相反地,如果 $foo 是一个 null值,或者是一个 false 值,表达式值为 false,没有输出。
#elseif和#else元素可以同#if一同使用。注意:Velocity 模板引擎遇到一个为true值的表达式就会停止。在下面的例子,假设
f
o
o
=
15
,
foo=15,
foo=15,bar=6:
#if( $foo < 10 )
Go North
#elseif( $foo == 10 )
Go East
#elseif( KaTeX parse error: Expected 'EOF', got '#' at position 42: …South</strong> #̲else <stron…foo比10大,所以在开始的两个比较中都失败。接着 $bar跟6比较是真的所以输出为 GO South。
注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,但是与java不同它使用“==”来比较两个值,而且velocity要求等号两边的值类型相同。
关系、逻辑运算符
Velocity中使用等号操作符判断两个变量的关系。这里有个简单例子关于等于号的使用:
#set (KaTeX parse error: Expected 'EOF', got '#' at position 32: …nucleic acid") #̲set (bar = “ribonucleic acid”)
#if ($foo == $bar)
In this case it’s clear they aren’t equivalent. So…
#else
They are not equivalent and this will be the output.
#end
Velocity有AND、OR和NOT逻辑运算符。想得到更多信息,请看VTL Reference Guide。下面的例子是说明AND、OR和NOT逻辑运算符的用法:
logical AND
#if( $foo && KaTeX parse error: Expected 'EOF', got '#' at position 42: … that</strong> #̲end 只有当foo 和 KaTeX parse error: Expected 'EOF', got '#' at position 12: bar 都为true,#̲if 才会得到true值。如果foo=false ,表达式的值为 false,$bar 就不会求值。如果 $foo的值为 true ,Velocity模板引擎会检查
b
a
r
的
值
,
如
果
bar的值,如果
bar的值,如果bar=true,整个表达式的值为true,输出为“This AND that”。如果$bar=false,整个表达式的值为false,没有输出。
逻辑OR 的工作方式一样,除了只要有一个值为true ,整个表达式的值就为true。考虑一下下面的例子。
logical OR
#if( $foo || KaTeX parse error: Expected 'EOF', got '#' at position 41: … That</strong> #̲end 如果foo=true,Velocity模板引擎就没有必要查找 $bar,无论
b
a
r
是
t
r
u
e
还
是
f
a
l
s
e
,
表
达
式
的
值
为
t
r
u
e
,
输
出
为
“
T
h
i
s
O
R
T
h
a
t
”
。
如
果
bar 是true 还是 false ,表达式的值为 true ,输出为“This OR That”。如果
bar是true还是false,表达式的值为true,输出为“ThisORThat”。如果foo=flase,$bar的值就一定要检查,在这个例子中,如果
b
a
r
同
样
是
f
a
l
s
e
,
表
达
式
的
值
为
f
a
l
s
e
,
没
有
输
出
。
从
另
外
一
个
角
度
看
,
如
果
bar 同样是false,表达式的值为 false ,没有输出。从另外一个角度看,如果
bar同样是false,表达式的值为false,没有输出。从另外一个角度看,如果bar 的值为true ,整个表达式的值为 true ,输出为 “This OR That”。
关于逻辑NOT ,只有一个疑问:
##logical NOT
#if( !$foo )
NOT that
#end
如果
f
o
o
=
t
r
u
e
,
!
foo=true, !
foo=true,!foo 的值为 false,没有输出。如果
f
o
o
=
f
a
l
s
e
,
!
foo=false,!
foo=false,!foo的值为true,输出为“NOT that”。注意不要跟 quiet reference $!foo 混为一谈,那是完全不一样的。
循环
Foreach循环
#foreach 用于循环。例子:
- #foreach( $product in $allProducts )
- $product
- #end
- #foreach( $key in $allProducts.keySet() )
- Key: $key -> Value: $allProducts.get($key)
- #end
$velocityCount | $customer.Name |
Default starting value of the loop
counter variable reference.
directive.foreach.counter.initial.value = 1
include
#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因,被引入的本地文件只能在TEMPLATE_ROOT目录下。
#include( “one.txt” )
#include 引用的文件用引号括起来。
如果您需要引入多个文件,可以用逗号分隔就行:
#include( “one.gif”,“two.txt”,“three.htm” )
在括号内可以是文件名,但是更多的时候是使用变量的。这用于根据页面提交的需求而输出。这里有一个例子同时有文件名和变量。
#include( “greetings.txt”, $seasonalstock )
parse
#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTL并render模板。
#parse( “me.vm” )
就像#include,#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是,#parse只能指定单个对象。
你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10。#parse是可以递归调用的,例如:如果dofoo.vm包含如下行:
Count down.
#set( $count = 8 )
#parse( “parsefoo.vm” )
All done with dofoo.vm!
那么在parsefoo.vm模板中,你可以包含如下VTL:
$count
#set( $count = $count - 1 )
#if( $count > 0 )
#parse( “parsefoo.vm” )
#else
All done with parsefoo.vm!
#end
在显示“Count down”后,Velocity通过parsefoo.vm,从8往下数。当计数到了0,它就会显示“All done with parsefoo.vm!”。在这时,Velocity 会返回到dofoo.vm ,输出信息:“All done with dofoo.vm!”。
停止
#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于debug是很有帮助的。
#stop
Velocimacros
#macro script element允许模板设计者定义一段可重用的VTL template。Velocimacros广泛用于简单和复杂的行列。Velocimacros的出现是为了减少编码和极小化排版错误,对Velocimacros的概念提供一个介绍。
#macro( d )
Superior |
Michigan |
Huron |
Erie |
Ontario |
volva |
stipe |
annulus |
gills |
pileus |
Velocimacro arguments
Velocimacro可以使用以下任何元素作为参数:
Reference:任何以KaTeX parse error: Unexpected character: '' at position 14: 开头的reference ̲ String literal…foo….$bar]
对象数组:[“a”,”b”,”c”]
boolean值:true、false
当将一个reference作为参数传递给Velocimacro时,请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro,而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如:
#macro( callme $a )
$a $a $a
#end
#callme( $foo.bar() )
执行的结果是:reference $foo的bar()方法被执行了三次。
在第一次扫描中,出现惊人的作用。但是当你考虑到Velocimacro的最原始动机:消除剪切、粘贴、复制,简单地使用VTL使它更灵活。它允许你对传递对象到Velocimacro,例如一个产生重复序列颜色,着色表格的行的对象。
如果你不需要这样的特性可以通过以下方法:
#set( $myval = $foo.bar() )
#callme( $myval )
Velocimacro properties
Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide。
Velocity.properties文件中的velocimacro.libraary:一个以逗号分隔的模板库列表。默认情况下,velocity查找唯一的一个库:VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。
Velocity.properties文件中的velocimacro.permissions.allow.inline属性:有两个可选的值true或者false,通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros。
Velocity.properties文件中的velocimacro.permissions.allow.inline.replace.global属性有两个可选值true和false,这个属性允许使用者确定inline的Velocimacro定义是否可以替代全局Velocimacro定义(比如在velocimacro.library属性中指定的文件内定义的Velocimacro)。默认情况下,此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。
Velocity.properties文件中的velocimacro.permissions.allow.inline.local.scale属性也是有true和false两个可选值,默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说,如果这个属性设置为true,一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM窍门:a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected。
Velocity.properties文件中的velocimacro.context.localscope属性有true和false两个可选值,默认值为false。当设置为true时,任何在Velocimacro内通过#set()对context的修改被认为是针对此velocimacro的本地设置,而不会永久的影响内容。
Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时,对于一个Velocimacro的调用将自动检查原始库是否发生了变化,如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果,就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态(file.resource.loader.cache = false)。注意这个属性实际上是针对开发而非产品的。
Velocimacro Trivia
Velocimacro必须被定义在他们被使用之前。也就是说,你的#macro()声明应该出现在使用Velocimacros之前。
特别要注意的是,如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。
Escaping VTL directives
VTL directives can be escaped with “\”号,使用方式跟VTL的reference使用逃逸符的格式差不多。
#include( “a.txt” ) renders as
#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” )
在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心(比如在一个if-else-end statement内)。下面是VTL的if-statement的典型应用:
#if( KaTeX parse error: Expected 'EOF', got '#' at position 31: …heslav Ganelin #̲end 如果jazz是ture,输出将是:
Vyacheslav Ganelin
如果$jazz是false,将没有输出。使用逃逸符将改变输出。考虑一下下面的情况:
#if(
j
a
z
z
)
V
y
a
c
h
e
s
l
a
v
G
a
n
e
l
i
n
#
e
n
d
现
在
无
论
jazz ) Vyacheslav Ganelin \#end 现在无论
jazz)VyacheslavGanelin#end现在无论jazz是true还是false,输出结果都是:
#if($ jazz )
Vyacheslav Ganelin
#end
事实上,由于你使用了逃逸符,$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的,例如:
\#if( KaTeX parse error: Expected 'EOF', got '#' at position 32: …slav Ganelin \\#̲end 以上程序的显示结果为:…jazz为false,那么将没有输出。注意:事情也有例外,如果script elements 没有完全的逃避。
\#if( $jazz )
Vyacheslave Ganelin
\#end
在这里#if 已经逃避了,但是#end没有,有太多的#end 的产生解析错误。
VTL:Formatting issues
尽管在此用户手册中VTL通常都开始一个新行,如下所示:
#set( $imperial = [“Munetaka”,“Koreyasu”,“Hisakira”,“Morikune”] )
#foreach( $shogun in $imperial )
KaTeX parse error: Expected 'EOF', got '#' at position 8: shogun #̲end 但是像下面这种写法…foo = [“KaTeX parse error: Expected 'EOF', got '#' at position 20: …nd ","a cake"])#̲foreach(a in
f
o
o
)
foo)
foo)a #end please.
上面的代码可以被改写为:
Send me
#set( $foo = [”$10 and ",“a cake”] )
#foreach( $a in $foo )
KaTeX parse error: Expected 'EOF', got '#' at position 3: a #̲end please. 或者 …foo = ["KaTeX parse error: Expected 'EOF', got '#' at position 38: … #̲foreach …a in
f
o
o
)
foo )
foo)a
#end please.
每一种的输出结果将一样。
其他特性和杂项
math
在模板中可以使用Velocity内建的算术函数,如:加、减、乘、除。下面的等式分别是:加、减、乘、除:
#set( $foo = $bar + 3 )
#set( $foo = $bar - 4 )
#set( $foo = $bar * 6 )
#set( $foo = $bar / 2 )
当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到:
#set( $foo = $bar % 5 )
在Velocity内使用数学计算公式时,只能使用像-n,-2,-1,0,1,2,n这样的整数,而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。
Range Operator
Range operator可以被用于与#set和#foreach statement联合使用。对于处理一个整型数组它是很有用的,Range operator具有以下构造形式:
[n…m]
m和n都必须是整型,而m是否大于n则无关紧要。例子:
First example:
#foreach( $foo in [1…5] )
$foo
#end
Second example:
#foreach( $bar in [2…-2] )
$bar
#end
Third example:
#set( $arr = [0…1] )
#foreach( $i in $arr )
$i
#end
Fourth example:
[1…3]
产生以下输出:
First example:
1 2 3 4 5
Second example:
2 1 0 -1 -2
Third example:
0 1
Fourth example:
[1…3]
注意:range operator只在#set和#foreach中有效。
网页设计者使用标准尺寸制作表格,但是某时候会没有足够的数据填入表格,就会发现 range 非常有用。
Advanced Issue:Escaping and!
当一个reference被“!”分隔时,并且在它之前有逃逸符时,reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下:
#set( $foo = “bar” )
$!foo
$!{foo}
$\!foo
$\!foo
这将显示为:
$!foo
$!{foo}
$!foo
$\!foo
\ 在 $ 前面跟逃避规则相比较;
$foo
$!foo
$!{foo}
\$!{foo}
这将显示为:
$foo
$!foo
$!{foo}
\bar
Velocimacro杂记
这章节是一个小型涉及Velocimacros的常见问题解答。这章节会随时间的变化而变化,所以它的作用有时是检验新的信息。
注意:在这章节’Velocimacro’简称为‘VM’。
- 我能用一个标识或者另外的VM 作为一个VM的参数吗?
例如:#center ( #bold( “hello” ) )
答:不可以。一个directive的参数使用另外一个directive是不合法的。
但是,还是有些事情你可以作的。最简单的方式就是使用双引号,所以你可以这样做:
#set($stuff = “#bold(‘hello’)” )
#center( $stuff )
上面的格式也可以缩写为一行:
#center( “#bold( ‘hello’ )” )
请注意在下面的例子中参数被evaluated在Velocimacro内部,而不是在calling level。例子:
#macro( inner $foo )
inner : $foo
#end
#macro( outer KaTeX parse error: Expected 'EOF', got '#' at position 10: foo ) #̲set(bar = “outerlala”)
outer : $foo
#end
#set(KaTeX parse error: Expected 'EOF', got '#' at position 23: …calltimelala') #̲outer( "#inner(bar)" )
输出结果为:
Outer : inner : outerlala
因为#inner(KaTeX parse error: Expected 'EOF', got '#' at position 11: bar)的赋值发生在#̲outer(),所以bar的值在#outer()赋给的。
记住Veloctiy的特性:参数的传递是By Name的。例如:
#macro( foo $color )
#foo( KaTeX parse error: Expected 'EOF', got '#' at position 76: …我们可以按照下面的方式执行: #̲set(color = $bar.rowColor())
#foo( $color )
2.我能通过#parse()登记velocimacros吗?
答:目前,Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。
如果你试图#parse()一个包含#macro() directive的模板,这一点是需要牢记的。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs。
3.什么是velocimacro自动加载?
答:velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false。
当.resource.loader.cache = false 连同一起设为true ()(是你用的资源器,例如‘file’),当你编译它们的时候Velocity引擎会自动重载那些在你的velocimacro库文件中的修改,所以你不用servlet引擎(或者应用程序)或者其他窍门来使你的velocimacros重载。
这里有一个简单的属性配置。
file.resource.loader.path = templates
file.resource.loader.cache = false
velocimacro.library.autoreload = true
不要在你的作品中出现上述的配置
字符串串联
开发人员最常问的问题是我如何作字符拼接?在java中是使用“+”号来完成的。
在VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如:
#set( $size = “Big” )
#set( $name = “Ben” )
The clock is $size$name.
输出结果将是:The clock is BigBen.。更有趣的情况是:
#set( $size = “Big” )
#set( $name = “Ben” )
#set($clock = "$size$name" )
The clock is $clock.
上例也会得到同样的结果。最后一个例子,当你希望混合固定字段到你的reference时,你需要使用标准格式:
#set( $size = “Big” )
#set( $name = “Ben” )
#set($clock = "${size}Tall$name" )
The clock is $clock.
输出结果是:The clock is BigTallBen.。使用这种格式主要是为了使得 s i z e 不 被 解 释 为 size不被解释为 size不被解释为sizeTall。
反馈
如果你在手册中遇到任何错误或者有其他关于Velocity用户手册的反馈,请email到 Velocity user list。谢谢!