现在的位置: 首页 > 综合 > 正文

Velocity User Guide 用户手册

2019年06月18日 ⁄ 综合 ⁄ 共 33357字 ⁄ 字号 评论关闭

转自:http://blog.csdn.net/gaojinshan/article/details/23945879

官方网址:http://velocity.apache.org/engine/devel/user-guide.html

关于本手册

本手册的目的在于:帮助页面设计人员和内容提供者,了解Velocity及其简单但强大的脚本语言(VTL,Velocity模板语言)的语法。本手册中的许多例子,演示的是使用Velocity,在Web页面中,嵌入动态内容,但所有的VTL例子,同样适用于其他任何的页面或模版。

谢谢选择Velocity!

Velocity是什么

Velocity是一个基于Java的模版引擎。它允许页面设计人员引用Java代码中定义的方法。Web页面设计人员,可以跟Java程序员并行工作:根据MVC模型,以开发Web页面;这意味着。Web页面设计人员能专注于创建更好的页面,而程序员能专注于编写优秀的代码。Velocity分离了Java代码和Web页面,它使得Web页面从长远来看更容易维护,它为JSP或PHP提供了可替代的方案。

Velocity可用来从模版中,生成Web页面,SQL,PostScript和其他输出。它即可作为独立的工具,用来生成源代码和报告;也可作为其他系统的一个集成组件。集成好后,Velocity将为Turbine Web应用框架提供模板服务。 Velocity+Turbine提供的模板服务,将可实现根据一个真正的MVC模型,开发Web应用。

Velocity能为我做什么?

泥品商店的例子

假设你是一个专门销售泥品的在线商店的页面设计人员。让我们就叫它“在线泥品商店”吧。生意蒸蒸日上。客户订购各种类型和质量的泥品。他们用他们的用户名和密码,登陆你们的网站后,可查看他们的订单,购买更多的泥品。现在,非常畅销的古铜色泥品开售了。你们的客户中,一少部分仍然照常购买大红色泥品,虽然它不够畅销,并且常常被摆在页面的角落里,但它仍在销售。每个客户的信息,都被记录在数据库中,因此,有一天,问题就来了,为什么不用Velocity,来标出那些顾客们最感兴趣的特价泥品呢?

Velocity使得为你的在线用户定制Web页面,变的很容易。作为一个在泥品商店的Web页面设计人员,你想创建客户登陆你们的页面后将看到页面。

你会见了你公司的软件工程师,他们都同意用$customer来保存当前登陆的用户的信息,用$mudsOnSpecial保存当前在售泥品的所有种类。$flogger对象包含了帮助促销的方法。接下来,让我们只关注这三个引用对象。记住,你不需要操心软件工程师是怎样从数据库中获取数据的,你只需要知道它能取到。这让你只管你的工作,而让软件工程师只管他们的。

你可以在Web页面中,嵌入如下的VTL语句:

<HTML>
<BODY>
Hello $customer.Name!
<table>
#foreach( $mud in $mudsOnSpecial )
   #if ( $customer.hasPurchased($mud) )
      <tr>
        <td>
          $flogger.getPromo( $mud )
        </td>
      </tr>
   #end
#end
</table>

关于foreach语句的具体细节,将会进行更深入而简短的描述。重要的是,这段简短的脚本,在你的Web页面上起的作用。当一个偏好大红色泥品的客户登陆后,并且大红色泥品还在售的话,它就是客户将会看到的,被显著地展示了出来。如果另外一个购买古铜色泥品很长时间的客户登陆后,古铜色泥品的通知,就会在最前并居中。Velocity的灵活性是极大的,只有你想不到,没有做不到。

在VTL参考手册中,还包含了许多其他的Velocity元素,它们将共同为你提供你创建Web页面时,所需的功能和灵活性。当你熟悉了这些元素后,你将会发挥Velocity的强大功能。

我应该使用什么jar包?

Velocity引擎2.x发布包,有好几个跟Maven有关的jar包,发布在Maven仓库。

Maven用户

在你的POM文件中,包含如下依赖关系:

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-core</artifactId>
  <version>x.x.x</version>
</dependency>   

如果你想将Velocity日志功能关联到Common logging,SLF4J,Log4j或servlet logger,请选择相应的依赖关系:

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-commons-logging</artifactId>
  <version>x.x.x</version>
</dependency>

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-slf4j</artifactId>
  <version>x.x.x</version>
</dependency>

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-log4j</artifactId>
  <version>x.x.x</version>
</dependency>

<dependency>
  <groupId>org.apache.velocity</groupId>
  <artifactId>velocity-engine-servlet</artifactId>
  <version>x.x.x</version>
</dependency>    

其他用户

首先,下载Velocity引擎发布包

如果你想快速启动你的应用,简单地将velocity-x.x.x.jar放在你的classpath下:它包含了所有Velocity引擎类(不含例子)。也可将jar包放在lib子目录下。

其他的jar包是些细粒度的jar包,它们包含了你可能会需要的功能。

  • velocity-engine-core-x.x.x.jar:主要的Velocity库。总是要包含它的。
  • velocity-engine-commons-logging-x.x.x.jar它包含了绑定Commons Logging,因此,所有的日志信息将会发送给它。跟Commons Logging一起使用(放在lib目录下)。
  • velocity-engine-slf4j-x.x.x.jar它包含了绑定SLF4J,因此,所有的日志信息将会发送给它。SLF4J一起使用(放在lib目录下)。
  • velocity-engine-log4j-x.x.x.jar它包含了绑定Log4j,因此,所有的日志信息将会发送给它。Log4j一起使用(放在lib目录下)。
  • velocity-engine-servlet-x.x.x.jar它包含了绑定servlet
    logger。在servlet容器中使用。

你总会用到的额外的jar包,是Commons Collections和Commons Lang,也放在lib目录下

Velocity模板语言(VTL):介绍

Velocity模版语言,旨在提供一个简单、普通、清晰的方式,来把动态的内容合并到Web页面中。即便是一个只具有一点甚至没有编程经验的页面开发员,也能迅速地掌握使用VTL并合并动态内容到Web页面的方法。

VTL使用引用(reference),来将动态内容嵌入到Web页面中,变量是引用的一种。变量能引用在Java代码中定义的一些对象,也能在Web页面内从VTL语句中获取其值。下面是一个可嵌入到HTML文档中的VTL语句的例子:

#set( $a = "Velocity" )

和所有的VTL语句一样,这个VTL语句,以#符号开头,并且包含一个指令:set。当一个在线的用户,请求这个Web页面时,Velocity模版引擎,会全面搜索你的Web页面,以查找所有的#符号,然后确定哪个标识了VTL语句的开头,哪个什么也不做。

#符号后面跟着一个指令,set。set指令,使用了一个表达式(包含在括号里)——一个把值赋给变量的等式。变量都是列在左边,值放在右边;两者被=号分开。

在上面的例子中,变量是$a,值是Velocity。该变量象所有引用一样,以$符号开头。字符串的值总是放在引号内,无论是单引号还是双引号。单引号保证被引用的值赋值给引用。双引号允许你使用Velocity引用和指令来插入值,比如"Hello
$name",这里的$name将被当前值所代替,然后,字符串字面量才被赋值给=号左边。

下面的规则,能让你更好地理解Velocity是怎样工作的:引用以$开头,用来得到东西。指令以#开头,用来做些事情。

在上面的例子中,#set用来给变量赋值。变量$a用来在模板中输出"Velocity"。

Hello Velocity World!

当一个变量被赋予了值,你可以在你的HTML文档中的任何位置引用它。在下面的例子中,变量$foo被赋了值,之后被引用。

<html>
<body>
#set( $foo = "Velocity" )
Hello $foo World!
</body>
<html>

结果是显示了“Hello Velocity World!”的Web页面。

为了让包含VTL指令的语句,更易读,我们建议你,每个VTL语句另起一行,虽然这并不是必须的。set指令,后面会详细介绍。

Comments注释

注释允许包含一些描述性的文本,它们并不会在模版引擎中输出。注释能有效地提醒你自己,或解释给别人,你的VTL语句在做什么,或者其他你发现有用的任何目的。下面是一个注释的例子:

## 这是单行注释 

单行注释以##开头,到行尾为止。如果你想写多行注释,那么,你没有必要写成很多单行的注释。以#*开头,以*#结束的多行注释,刚好可用于这个场景。

这是多行注释以外的文本,在线用户能看见它。
#*
 这些是多行注释,在线用户看不见它,
 因为Velocity模板引擎会忽略掉它。
*#
这里的文本在多行注释以外;它是可见的。

下面是些演示单行注释和多行注释是如何工作的例子:

这里是可见的。 ## 这里不可见。
这里是可见的。 
这里是可见的。 #* 这里是多行注释的一部分,不可见。

这里也不可见;它也是多行注释的一部分。

仍然不可见。 *# 这里在多行注释外面了,因此是可见的。
## 这里不可见。

这是第三种类型的注释,VTL注释块,它用来保存你想在模板中跟踪的各种额外信息(比如文档作者和版本信息):

#**
这是VTL注释块,可用户保存这些信息,比如作者和版本信息:
@author
@version 5
*#

References引用

在VTL中,有三种类型的引用:变量,属性和方法。作为一个使用VTL的设计者,你和你的工程师,必须在引用的名字上,达成一致的标准,这样你才能在模板中正确地使用它们。

变量
简单来说,变量就是以$开头,后跟一个VTL标示符。一个VTL标示符是以一个字母开头,后跟下列字符:

  • 字母(a .. z, A .. Z)
  • 数字(0 .. 9)
  • 连字符("-")
  • 下划线("_")

下面是VTL中一些合法的变量引用的例子:

$foo
$mudSlinger
$mud-slinger
$mud_slinger
$mudSlinger1

当VTL引用了一个变量,比如$foo,则该变量能从模板中的set指令,或者从Java代码中得到值。比如,一个Java变量$foo有个值bar,则当模版被请求的时候,bar会代替页面中所有的$foo变量。此外,如果我包含了语句:

#set( $foo = "bar" )

那么该指令后面的所有的$foo实例同样会被替换。

属性

VTL引用的第二种类型是属性(properties),属性都有其特殊的格式。它是由如下几部分组成:以符号$开头,跟着一个VTL标示符,接着是一个逗号和另一个VTL标示符。下面是一些在VTL中的合法属性引用的例子:

$customer.Address
$purchase.Total

第一个例子中,$customer.Address,它有两个意思。它可以表示,在哈希表($customer)中查找,并返回关键字Address关联的值;另外,$customer.Address也代表一个方法(其代表的方法,将在下一节中介绍)。$customer.Address,也可作为$customer.getAddress()的简写。当你的页面被请求时,Velocity会决定这两种情况的哪一种是最合理的,并返回适当的值。

方法
在Java代码中定义的方法,能做一些有用的事情,比如执行计算或者作出某种决定。方法是这样的引用:以符号$开头,后跟一个合法的标示符,跟着是一个VTL方法体。一个VTL方法体是由一个合法的VTL标示符,跟着一个左括号,跟着一个可选的参数列表,跟着一个右括号等组成的。下面是一些在VTL中的合法的VTL方法引用的例子:

$customer.getAddress()
$purchase.getTotal()
$page.setTitle( "My Home Page" )
$person.setAttributes( ["Strange", "Weird", "Excited"] )

前面的两个例子——$customer.getAddress()$purchase.getTotal()——可能看起来跟上述的属性章节中的$customer.Address$purchase.Total很相似。如果你猜这些例子在某种方式上肯定有相关性,那么你猜对了。

VTL属性可作为VTL方法的简写。属性$customer.Address和方法$customer.getAddress()的作用,完全一样。通常,在能用属性时,优先用属性。属性和方法的主要区别,是可以为方法指定一个参数列表。

下列的方法,可以简写:

$sun.getPlanets()
$annelid.getDirt()
$album.getPhoto()

我们希望这些方法,返回太阳系中所有行星的名称,喂养我们的蚯蚓,或者从相册里获取照片。下面的方法,只能用完整的方式。

$sun.getPlanet( ["Earth", "Mars", "Neptune"] )
## 不能传递参数列表给$sun.Planets

$sisyphus.pushRock()
## Velocity假定我的意思是$sisyphus.getRock()

$book.setTitle( "Homage to Catalonia" )
## 不能传递参数

作为Velocity 1.6,所有数组引用,目前都被作为固定长度的列表。这就意味着,你对数组引用,可调用java.util.List方法。因此,如果你有一个数组引用(让我们假设,这个数组有3个元素,是String[]类型的),你能这样做:

$myarray.isEmpty()
$myarray.size()
$myarray.get(2)
$myarray.set(1, 'test')

Velocity 1.6也新增支持了可变参数的方法。一个方法像public void setPlanets(String... planets)或者public void setPlanets(String[] planets)(如果你用JDK5以前的版本),当在模板中调用它时,现在它能接受任何数量的参数。

$sun.setPlanets('Earth', 'Mars', 'Neptune')
$sun.setPlanets('Mercury')
$sun.setPlanets()
## 只能传递空的,0长度的数组

属性查找规则

如前面所说,属性常常是指父对象的方法。Velocity很聪明,能知道哪个方法对应于哪个请求的属性。它会尝试各种基于几种命名规范的可选方案。具体的查找顺序,依赖于属性名是否以大写字母开头。对于小写字母,比如$customer.address,查找顺序是:

  1. getaddress()
  2. getAddress()
  3. get("address")
  4. isAddress()

对于大写字母开头的属性,如$customer.Address,就有些不同了:

  1. getAddress()
  2. getaddress()
  3. get("Address")
  4. isAddress()

最终转换
当最终输出的时候,每个引用的最终的值(不管是变量,属性,还是方法),都会被转换成String对象。如果$foo表示一个对象(比如一个整型对象),Velocity会调用它的.toString()方法将该对象转换为String对象。

索引表示法
使用这个形式的表示法$foo[0]能访问对象的索引。这种形式,跟调用指定对象的get(Object)方法一样,如$foo.get(0),提供了该操作一种简写形式。因此,下面这些都是有效的

$foo[0]       ## $foo能接受一个数值
$foo[$i]      ## 使用另外一个应用作为索引
$foo["bar"]   ## 传递一个字符串,$foo应该是个Map

中括号表示法可以操作Java数组,因此,Velocity为对象的get(Integer)方法,也提供这种用法,以返回指定的元素。

当用.get有效的地方,用中括号表示法,也是有效的,比如下面的例子:

$foo.bar[1].junk
$foo.callMethod()[1]
$foo["apple"][4]

引用也可使用索引表示法,比如:

#set($foo[0] = 1)
#set($foo.bar[1] = 3)
#set($map["apple"] = "orange")

指定的元素,被设置为给定的值。Velocity先对元素尝试'set'方法,然后用'put'赋值。

正规的引用格式
引用的缩写,上面的例子已经给出了,但引用的正规格式,下面将会演示:

${mudSlinger}
${customer.Address}
${purchase.getTotal()}

通常情况下,我们会使用引用的简写方式,但在有些情况下,要用正规形式以保证正确处理。

假设你要用$vice作为名词,来造个句子。目标是允许别人选择一个基础词根,造出下面两个句子中的一个:"Jack is a pyromaniac."或者"Jack is a kleptomaniac."。对这种情况,用引用的缩写方式,就不行了。考虑下面的例子:

Jack is a $vicemaniac.

这里有歧义,Velocity会认为你想使用的标示符是$vicemaniac,而不是$vice。找不到$vicemaniac的值,就会返回$vicemaniac。用正规的写法能解决这个问题。

Jack is a ${vice}maniac.

现在,Velocity知道$vice是引用,而不是$vicemaniac。在模板中,当引用直接和文本相邻的时候,正规写法通常很有用。
静态引用表示法

当Velocity遇到未定义的引用时,通常的做法是直接返回这个引用字面值。例如,假设下面的应用作为VTL模板的一部分出现:

<input type="text" name="email" value="$email"/>

当表单开始加载时,引用变量$email并没有值,但你更期望在显示"$email"的地方,是一个空白文本框。使用静态引用表示法,能改变Velocity的正常行为;在VTL模板中,使用$!email来代替$email。那么,上面的例子,会像下面这样:

<input type="text" name="email" value="$!email"/>

现在,当表单开始加载时,$email引用并没有值,输出的将会是一个空白的字符串,而不是"$email"。

正规的和静态的引用表示法,能一起使用,演示如下:

<input type="text" name="email" value="$!{email}"/>

严格引用模式

Velocity 1.6介绍了严格引用模式的概念,它可以通过设置Veloctiy配置属性'runtime.references.strict'为true来激活。这个设置的常用目的是,在未定义或有歧义的情况下,使得Velocity的行为更严格,更像一个编程语言,这更适合Velocity的一些用户。在这种未定义或有歧义的情况下,Velocity将会抛出异常。下面讨论在严格行为下与在传统行为下,有所不同的基本情况。

设置后,引用需要显式地设置内容,或用#set指令定义,否则,Velocity将会抛出异常。有null值的引用,不会产生异常。此外,如果试图调用一个对象的未定义的方法或属性,Velocity将会抛出异常。如果试图调用值为null的方法或属性,也是这样的。
下面的列子中,$bar定义了,但$foo未定义,所有的语句都会抛出异常:

$foo                         ## 异常
#set($bar = $foo)            ## 异常
#if($foo == $bar)#end        ## 异常
#foreach($item in $foo)#end  ## 异常

同样,下面的语句,演示了这样的例子:当去调用不存在的方法或属性时,Velocity将抛出异常。在这些例子中,$bar包含了一个对象,它定义了一个返回字符串的属性'foo',和返回null的'retnull'。

$bar.bogus          ## $bar没有提供属性bogus,异常
$bar.foo.bogus      ## $bar.foo没有提供属性bogus,异常
$bar.retnull.bogus  ## 不能调用值为null的属性,异常

通常,在所有情况下,除了特殊的#if指令的情况,引用使用的都是严格引用行为。如果在#if或#elseif指令中,没有任何方法或属性的引用,并且它不跟其他值做比较,未定义的引用,是允许的。这种行为,提供了一个简单的方法,来测试在模板中,引用是否先定义再使用。下面的例子中,$foo未定义,这些语句并不会抛出异常。

#if ($foo)#end                  ## False
#if ( ! $foo)#end               ## True
#if ($foo && $foo.bar)#end      ## False and $foo.bar will not be evaluated
#if ($foo && $foo == "bar")#end ## False and $foo == "bar" wil not be evaluated
#if ($foo1 || $foo2)#end        ## False $foo1 and $foo2 are not defined

严格模式下,在#if指令中使用比较运算符(>,<,>= 或<=)是有意义的。同样,#foreach的参数必须是可迭代的(这个行为,可通过directive.foreach.skip.invalid属性修改)。最后,在严格模式下,未定义的宏引用,也总会抛出异常。

Velocity试图返回值为null的引用,将导致异常。 这种情况下,简单返回空的办法是,使用'$!'代替'$',跟非严格模式类似。记住,这是不同的:当上下文中不存在引用,在严格模式下,试图返回它时,将总会抛出异常。例如,在上下文中,下面的$foo是个null值。

this is $foo    ## 抛出异常,因为$foo为null
this is $!foo   ## 返回"this is ",没有异常
this is $!bogus ## bogus没在上下文中,因此,抛出异常

替代情况

现在,你对引用已经熟悉了,你能在模板中有效地使用它们了。Velocity引用利用了一些Java原则,模板设计者会发现它更易用。
比如:

$foo

$foo.getBar()
## 等价于
$foo.Bar

$data.setUser("jon")
## 等价于
#set( $data.User = "jon" )

$data.getRequest().getServerName()
## 等价于
$data.Request.ServerName
## 等价于
${data.Request.ServerName}

这些例子演示了一些引用的可选方案。Velocity利用了Java的内检(introspection)和bean特性,来引用上下文中的对象或对象方法。 几乎在模板中任何地方,都可以嵌入和计算引用。

Velocity,模仿了Sun微系统中定义的Bean规范,是大小写敏感的;不管怎样,它的开发者努力发现并校正用户的任何可能的错误。当模板中的方法getFoo()$bar.foo用到,Velocity会先尝试$getfoo。如果失败了,然后回尝试$getFoo。同样地,当模板中用到$bar.Foo,Velocity会先尝试$getFoo() 再尝试getfoo().

注意:模板中不能引用实例变量。只能引用JavaBean getter/setter方法对应的属性。(比如,$foo.Name能引用类Foo的getName()实例方法,但不能引用Foo的Name实例变量)。

Directives指令

引用允许模板设计者为Web页面生成动态内容,因为指令——简单使用脚本元素,来创造性地操作Java代码的输出——允许Web设计者,真正地管理Web页面的外观和内容。

指令都是以#开头的。像引用易用,指令的名称可以用{和}括起来。当指令后紧跟着文本时,这很有用。例如,下面会报错:

#if($a==1)true enough#elseno way!#end

这种情况下,就要用括号将#else和后面的内容分开。

#if($a==1)true enough#{else}no way!#end

#set

#set指令用来为引用赋值。值可别赋给变量引用,或属性引用;要用括号括起来,比如:

#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )

赋值等式的左侧必须是变量引用或属性引用。右侧必须是下面的类型:

  • 变量引用
  • 字符串常量
  • 属性引用
  • 方法引用
  • 数值常量
  • ArrayList
  • Map

这些列子演示上述的各种类型:

#set( $monkey = $bill ) ## 变量引用
#set( $monkey.Friend = "monica" ) ## 字符串常量
#set( $monkey.Blame = $whitehouse.Leak ) ## 属性引用
#set( $monkey.Plan = $spindoctor.weave($web) ) ## 方法引用
#set( $monkey.Number = 123 ) ##数值常量
#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map

注意:ArrayList的例子,通过[..]操作符定义的元素,可以使用ArrayList类定义的方法访问。因此,你可以用$monkey.Say.get(0)访问第一个元素。

同样,Map的例子, 通过{}操作符定义的元素,也可以使用Map类定义的方法访问。因此,你可以用$monkey.Map.get("banana")访问第一个元素,得到字符串'good',$monkey.Map.banana也返回相同的值。

等式右侧也可以是简单的算术表达式:

#set( $value = $foo + 1 )
#set( $value = $bar - 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )

如果右侧是值为null的属性或方法应用,它不会被赋到左侧。根据Velocity的特性,通常不能利用这种机制,从上下文中移除一个已经存在的引用。(注意,修改Velocity的配置属性,是可以的)。这会让初学者感到困惑。例如:

#set( $result = $query.criteria("name") )
第一次query的结果是$result
#set( $result = $query.criteria("address") )
第二次query的结果是$result

如果$query.criteria("name") 返回字符串"bill",并且$query.criteria("address")返回null,,上述VTL将获得如下结果:

第一次query的结果是bill
第二次query的结果是bill

这也让初学者感到困惑:他创建#foreach循环,通过属性或方法引用#set引用,然后立即用#if指令测试它。比如:

#set( $criteria = ["name", "address"] )
#foreach( $criterion in $criteria )
    #set( $result = $query.criteria($criterion) )
    #if( $result )
        Query was successful
    #end
#end

上述的例子中,对$result的求值,取决于查询是否成功,这是不明智的。$result#set(添加到上下文)后,就不能再置null(从上下文中删除)。指令#if#foreach的详细描述,本文档的后面,将会提到。

这种情况的一个解决方案是:先把$result设置为false。然后,你可检查$query.criteria()调用是否失败。

#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语句。

常量

使用#set指令时,用双引号起来的字符串常量,会被解析和处理,显示如下:

#set( $directoryRoot = "www" )
#set( $templateName = "index.vm" )
#set( $template = "$directoryRoot/$templateName" )
$template

输出会是:

www/index.vm

然而,单引号引起来的字符串常量,不会被解析:

#set( $foo = "bar" )
$foo
#set( $blargh = '$foo' )
$blargh

结果是:

  bar
  $foo

默认情况下,这种用单引号引起来的不会被解析的特性,在Velocity中是可用的。可以通过编辑velocity.properties ,修改这种默认,比如stringliterals.interpolate=false。

#[[don't parse me!]]#的语法,允许模板设计者,在模板中方便地使用大块的无需翻译或解析的内容。在代替那些无效VTL内容(因此也不可解析)的转义多个指令或转义段时,这尤其有用。

#[[
#foreach ($woogie in $boogie)
  对于$woogie,什么也不会发生
#end
]]#

结果是:

#foreach ($woogie in $boogie)  
  对于$woogie,什么也不会发生

#end

条件语句

If / ElseIf / Else

在if语句为true的情况下,Velocity中的#if指令,允许Web页面生成时包含文本。例如:

#if( $foo )
   <strong>Velocity!</strong>
#end

变量$foo被计算,以确定是否为true。这可能是如下的三种情况之一:

  • $foo 是boolean (true/false),值是true
  • $foo 是stringcollection,值不为null也不为空
  • $foo 是对象(不是string或collection),值不为null

记住,Velocity上下文只能包含对象,因此,当我们说'boolean',它就是指Boolean类。真是这样的,返回boolean的方法,内检(introspection)机制将返回相同逻辑值的Boolean

如果计算的值是true,在#if#end语句之间的内容会被输出。这种情况下,如果$foo是true,输出会是:"Velocity!"。相反,如果$foo 为null值,或者它是值为false的boolean类型,计算的值是false,并且没有输出。

#elseif 或 #else 元素,可跟#if 元素一起使用。注意,Velocity模板引擎,将在第一个值为true的表达式那里停止。下面的例子中,假设$foo 值为15,$bar 值为6。

#if( $foo < 10 )
    <strong>Go North</strong>
#elseif( $foo == 10 )
    <strong>Go East</strong>
#elseif( $bar == 6 )
    <strong>Go South</strong>
#else
    <strong>Go West</strong>
#end

在这个例子中,$foo 比10大,因此,前两个比较都失败。接着,$bar与6比较,值为true,所以,输出是Go South

关系和逻辑运算符

Velocity使用等号运算符,来决定变量之间的关系。下面是个简单的例子,来演示等号运算符是怎么使用的。

#set ($foo = "deoxyribonucleic acid")
#set ($bar = "ribonucleic acid")

#if ($foo == $bar)
  这种情况,很明显它们是相等的,因此。。。
#else
  它们不相等,这行会被输出。
#end

注意,==的含义跟Java里的==有点不同,Java中==只能进行对象比较。在Velocity中,等号运算符,可被用来直接比较数值,字符串,或者对象。当对象不是同一类时,每个对象都会先用toString()获取其字符串,然后进行比较。

Velocity也有逻辑运算符AND,OR和NOT。下面是演示逻辑运算符AND,OR和NOT的例子。

## 逻辑AND
#if( $foo && $bar )
   <strong> This AND that</strong>
#end

仅当$foo$bar都true时,#if()指令的值才为true。如果$foo是false,表达式的值是false;$bar不再计算。
如果$foo是true,Velocity模板引擎将检查$bar的值;如果$bar是true,这个表达式的值是true,This AND that 会被输出。如果$bar是false,整个表达式是false,没有输出。

逻辑运算符OR也同样工作,只要一个引用的值为true,整个表达式的值就是true。看下面的例子。

## 逻辑OR
#if( $foo || $bar )
    <strong>This OR That</strong>
#end

如果$foo是true,Velocity模板引起不会管$bar了;不管$bar是true或false,表达式的值都是true,This OR That会输出。如果$foo是false,那么,$bar 必须检查。这种情况下,如果$bar也是false,表达式的值是false,没有输出。相反,如果$bar是true,整个表达式的值是true,并且输出This
OR That

逻辑运算符NOT,只有一个参数:

##逻辑NOT
#if( !$foo )
  <strong>NOT that</strong>
#end

这里,如果$foo是true,那么!$foo就是false,就没输出。如果$foo是false,那么!$foo就是true,并且NOT that会输出。注意不要将这和静态引用$!foo搞混了,它可完全不同。

所有逻辑操作符,还有文本版本,包括eq,ne,and,or,not,gt,ge,ltle

更多有用注意事项。当你想在#else指令后,紧跟文本时,你需要使用大括号,将指令括起来,来区分它和后面的文本。(所有的指令,都可以用大括号括起来,尽管这样做没有比将#else括起来有用)。

#if( $foo == $bar)it's true!#{else}it's not!#end</li>

循环语句

Foreach循环

#foreach元素允许循环。例如:

<ul>
#foreach( $product in $allProducts )
    <li>$product</li>
#end
</ul>

这个#foreach循环,能循环访问$allProducts列表(对象)中的所有产品。每次循环,将$allProducts中的值,赋值给$product变量。

变量$allProducts的内容是个Vector,Hashtable或Array。赋给变量$product是个Java对象,并且可以像这样通过变量来引用。例如,假如$product是Java中的Product类,可以通过$product.Name方法($Product.getName())获取到它的名称。

假设$allProducts是个Hashtable。如果你想获取Hashtable的键值,就像获取Hashtable中的对象一样,你可以这样编码:

<ul>
#foreach( $key in $allProducts.keySet() )
    <li>Key: $key -> Value: $allProducts.get($key)</li>
#end
</ul>

Velocity提供了一个获取循环计数的简单方,因此,你可以这样做:

<table>
#foreach( $customer in $customerList )
    <tr><td>$foreach.count</td><td>$customer.Name</td></tr>
#end
</table>

Velocity也提供了一个告诉你是否为循环的最后一个元素的简单方法:

#foreach( $customer in $customerList )
    $customer.Name#if( $foreach.hasNext ),#end
#end

如果你想得到循环的一个以0开始的索引,你可用$foreach.index来代替$foreach.count。同样地,$foreach.first和$foreach.last可以跟$foreach.hasNext一起使用。如果你想访问外层#foreach循环的属性,你可以直接通过$foreach.parent或$foreach.topmost引用它们的属性(例如,$foreach.parent.index或$foreach.topmost.hasNext)。

可以设置循环执行的最大次数。默认情况,没有最大次数(设置为0或负数),但这可在velocity.properties中设置为任意数值。作为失效安全,这很有用。

# 允许的最大循环次数
directive.foreach.maxloops = -1

如果你想在模板中终止循环,你可在任何时候,用#break指令终止它。

## 仅列出前5个顾客
#foreach( $customer in $customerList )
    #if( $foreach.count > 5 )
        #break
    #end
    $customer.Name
#end

Include包含

#include脚本元素允许模板设计者导入一个本地文件,它会被插入到#include指令所在的位置。文件的内容,不会被模板引擎处理(render)。出于安全考虑,被导入的文件,只能被放在TEMPLATE_ROOT目录下。

#include( "one.txt" )

#include指令表明的文件,要用引号引起来。如果要导入多个文件,它们应该用逗号分开。

#include( "one.gif","two.txt","three.htm" )

将被导入的文件,无需用文件名来引用;事实上,常用变量来代替文件名。当页面请求提交后,根据它确定的标准,来定位输出时,这就很有用了。这里有个演示同时使用文件名和变量的例子。

#include( "greetings.txt", $seasonalstock )

Parse解析

#parse脚本元素允许模板设计者导入包含VTL的本地文件。Velocity将解析VTL并处理制定的模板。

#parse( "me.vm" )

#include指令一样,#parse也能用变量导入模板。#parse引用的任何模板,必须包含在TEMPLATE_ROOT目录下。跟#include指令不同的是#parse只能有一个参数。

VTL模板允许#parse语句导入的模板,也包含#parse语句。默认情况下,velocity.properties中的directive.parse.max.depth值是10,它也允许用户自定义。(注意:如果velocity.properties文件中的directive.parse.max.depth未设置,Velocity会将它设置为缺省值10)。递归是允许的,例如,模板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!"的信息。

Break中止

#break指令停止当前执行返回的继续处理。执行范围("execution scope")是指各种指令的内容(例如,#foreach,#parse,#evaluate,#define,#macro,或#@somebodymacro)或任何根("root")范围(template.merge(...),Velocity.evaluate(...)或velocityEngine.evaluate(...)) 跟#stop不同,#break仅中止最里层,直接范围,不是所有。

如果你想中止跳出特定的执行范围,不必是最直接的范围,那么,你可将范围控制引用(例如$foreach,$template,$evaluate,$define,$macro,或$somebodymacro)作为参数传递给#break。(例如#break($macro))。这会停止所有范围的处理,直到退出到指定的那个。在同样类型的嵌套范围内,记住,你总可以通过$<scope>访问父范围。父范围或$<scope>.topmost传递给#break(例如#break($foreach.parent)或#break($macro.topmost))。

Stop停止

#stop指令停止模板继续处理或执行。即使指令是通过#parse或在Velocity宏定位,嵌套在其他模板中,也是这样的。输出结果,会包含遇到#stop指令以前的所有内容。这方便从模板中退出。为了调试,你可能会提供一个信息参数(比如#stop('$foo
was not in context')),当停止命令完成后,它将会被写进日志(当然是DEBUG级别)。

Evaluate求值

#evaluate指令,用于动态计算VTL。它允许模板计算处理时生成的字符串,比如,用来模板国际化的字符串,或者从数据库中取数据的模板部分。

下面的例子会显示abc。

#set($source1 = "abc")
#set($select = "1")
#set($dynamicsource = "$source$select")
## $dynamicsource 现在是字符串'$source1'
#evaluate($dynamicsource)

Define定义

#define指令,可以为VTL的一整块内容,赋值给引用。

下面的例子将会显示Hello World!.

#define( $block )Hello $who#end
#set( $who = 'World!' )
$block

Macros宏

#macro脚本元素,允许模板设计者定义VTL模板重复的片段。宏在简单场景或复杂场景的广大范围内,非常有用。这个,作为介绍概念的例子,为了保存击键,减少输入错误而创建。

#macro( d )
<tr><td></td></tr>
#end

这个例子定义了宏d,并且可以想调用其他VTL指令一样,调用它:

#d()

当调用了这个模板时,Velocity会用一组单个的空数据单元格,来代替#d()。如果我们想在单元格里放些东西,我们可以修改这个宏,来允许它有数据体:

#macro( d )
<tr><td>$!bodyContent</td></tr>
#end

现在,如果我们调用这个宏,有点不同了。在其名称前使用#@,并且提供一个数据体,然后才是#end。 Velocity获取到$!bodyContent后,将会处理这个数据体:

#@d()Hello!#end

你仍能向之前那样调用它,只要我们用静态引用方式,标记数据体引用就行了(用$!bodyContent替换$bodyContent),它仍将返回一个空白数据单元格。

宏可以有任何数量的参数——甚至0个参数也行,像第一个例子那样——但是,当宏被调用时,必须像预先定义的那样,带有同样数量的参数。很多宏都比上面定义的要复杂。这里是个带有两个参数的宏,颜色和数组。

#macro( tablerows $color $somelist )
#foreach( $something in $somelist )
    <tr><td bgcolor=$color>$something</td></tr>
#end
#end

这个例子中,定义的宏,tablerows,带有两个参数。第一个参数是$color,第二个参数是$somelist

任何可以放在VTL模板中的东西,都可以放在宏的数据体中。宏tablerows是个foreach语句。宏#tablerows的定义中,有两个#end语句;第一个是#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>

在Velocity模板中,宏可以定义为内联(inline),也就是同一个页面中的其它Velocity模板是不能用它的。定义一个可以被其它模板共用的宏,有明显的好处:它能减少在大量模板中重复定义它;节省工作,并且减少出错的几率,还能保证对宏进行的一次修改,可被用于其它模板中。

如果#tablerows($color $list)定义在模版库中,这个宏就可在任何常规模版中使用了。它能被多次用于各种不同的目的。在模版mushroom.vm中,定义了各种蘑菇,调用#tablerows可列出一个典型的蘑菇的组成:

#set( $parts = ["volva","stipe","annulus","gills","pileus"] )
#set( $cellbgcol = "#CC00FF" )
<table>
#tablerows( $cellbgcol $parts )
</table>

当调用mushroom.vm时,Velocity会在模板库(定义在velocity.properties文件中)中发现宏#tablerows,并产生如下输出:

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

宏的参数

Velocimacros can take as arguments any of the following VTL elements :

  • Reference : anything that starts with '$'
  • String literal : something like "$foo" or 'hello'
  • Number literal : 1, 2 etc
  • IntegerRange : [ 1..2] or [$foo .. $bar]
  • ObjectArray : [ "a", "b", "c"]
  • boolean value true
  • boolean value false

When passing references as arguments to Velocimacros, please note that references are passed 'by name'. This means that their value is 'generated' at each use inside the Velocimacro. This feature allows you to pass references with
method calls and have the method called at each use. For example, when calling the following Velocimacro as shown

     #macro( callme $a )
         $a $a $a
     #end

     #callme( $foo.bar() )   

results in the method bar() of the reference $foo being called 3 times.

At first glance, this feature appears surprising, but when you take into consideration the original motivation behind Velocimacros -- to eliminate cut'n'paste duplication of commonly used VTL -- it makes sense. It allows you to
do things like pass stateful objects, such as an object that generates colors in a repeating sequence for coloring table rows, into the Velocimacro.

If you need to circumvent this feature, you can always just get the value from the method as a new reference and pass that :

     #set( $myval = $foo.bar() )
     #callme( $myval )   

宏的属性

Several lines in the velocity.properties file allow for flexible implementation of Velocimacros. Note that these are also documented in the Developer
Guide
.

velocimacro.library - A comma-separated list of all Velocimacro template libraries. By default, Velocity looks for a single library: VM_global_library.vm. The
configured template path is used to find the Velocimacro libraries.

velocimacro.permissions.allow.inline - This property, which has possible values of true or false, determines whether Velocimacros can be defined in regular templates.
The default, true, allows template designers to define Velocimacros in the templates themselves.

velocimacro.permissions.allow.inline.to.replace.global - With possible values of true or false, this property allows the user to specify if a Velocimacro defined inline
in a template can replace a globally defined template, one that was loaded on startup via the velocimacro.library property. The default, false,
prevents Velocimacros defined inline in a template from replacing those defined in the template libraries loaded at startup.

velocimacro.permissions.allow.inline.local.scope - This property, with possible values of true or false, defaulting to false, controls if Velocimacros defined inline are
'visible' only to the defining template. In other words, with this property set to true, a template can define inline VMs that are usable only by the defining template. You can use this for fancy VM tricks - if a global VM calls another global VM, with inline
scope, 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.

velocimacro.library.autoreload - This property controls Velocimacro library autoloading. The default value is false.
When set to true the source Velocimacro library for an invoked Velocimacro will be checked for changes, and reloaded if necessary. This allows you to change and test Velocimacro libraries without
having to restart your application or servlet container, just like you can with regular templates. This mode only works when caching is off in the resource loaders (e.g.file.resource.loader.cache
= false
 ). This feature is intended for development, not for production.

Getting literal

VTL uses special characters, such as $ and #, to do its work, so some added care should be taken where using these characters in your templates. This section deals with escaping these characters.

Currency 
There is no problem writing "I bought a 4 lb. sack of potatoes at the farmer's market for only $2.50!" As mentioned, a VTL identifier always begins with an upper- or lowercase letter, so $2.50 would not be mistaken for a reference.

Escaping Valid VTL References 
Cases may arise where you do not want to have a reference rendered by Velocity. Escaping special characters is the best way to output VTL's special characters in these situations, and this can be done using the backslash ( \ ) character when
those special characters are part of a valid VTL reference
*

#set( $email = "foo" )
$email

If Velocity encounters a reference in your VTL template to $email, it will search the Context for a corresponding value. Here the output will be foo, because $email is defined. If $email is not
defined, the output will be $email.

Suppose that $email is defined (for example, if it has the value foo), and that you want to output $email. There are a few ways of doing this, but the simplest is to use the escape character. Here is a
demonstration:

## The following line defines $email in this template:
#set( $email = "foo" )
$email
\$email

renders as

foo
$email

If, for some reason, you need a backslash before either line above, you can do the following:

## The following line defines $email in this template:
#set( $email = "foo" )
\\$email
\\\$email

which renders as

\foo
\$email

Note that the \ character bind to the $ from the left. The bind-from-left rule causes \\\$email to render as \$email. Compare these examples to those in which $email is not defined.

$email
\$email
\\$email
\\\$email

renders as

$email
\$email
\\$email
\\\$email

Notice Velocity handles references that are defined differently from those that have not been defined. Here is a set directive that gives $foo the value gibbous.

#set( $foo = "gibbous" )
$moon = $foo

The output will be: $moon = gibbous -- where $moon is output as a literal because it is undefined and gibbous is output in place of $foo.

Escaping Invalid VTL References 
Sometimes Velocity has trouble parsing your template when it encounters an "invalid reference" that you never intended to be a reference at all. Escaping special characters is, again, the best way to handle these situations, but in these situations,
the backslash will likely fail you. Instead of simply trying to escape the problematic $ or #, you should probably just replace this:

${my:invalid:non:reference}

with something like this

#set( $D = '$' )
${D}{my:invalid:non:reference}

You can, of course, put your $ or # string directly into the context from your java code (e.g. context.put("D","$");)
to avoid the extra #set() directive in your template(s). Or, if you are using VelocityTools,
you can just use the EscapeTool like this:

${esc.d}{my:invalid:non:reference}

Escaping of both valid and invalid VTL directives is handled in much the same manner; this is described in more detail in the Directives section.

Escaping VTL Directives 
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" )

Extra care should be taken when escaping VTL directives that contain multiple script elements in a single directive (such as in an if-else-end statements). Here is a typical VTL if-statement:

#if( $jazz )
    Vyacheslav Ganelin
#end

If $jazz is true, the output is

Vyacheslav Ganelin

If $jazz is false, there is no output. Escaping script elements alters the output. Consider the following case:

\#if( $jazz )
    Vyacheslav Ganelin
\#end

This causes the directives to be escaped, but the rendering of $jazz proceeds as normal. So, if $jazz is true, the output is

 #if( true )
     Vyacheslav Ganelin
 #end
 

Suppose backslashes precede script elements that are legitimately escaped:

\\#if( $jazz )
   Vyacheslav Ganelin
\\#end

In this case, if $jazz is true, the output is

\ Vyacheslav Ganelin
\

To understand this, note that the #if( arg ) when ended by a newline (return) will omit the newline from the output. Therefore, the body of the #if() block
follows the first '\', rendered from the '\\' preceding the #if(). The last \ is on a different line than the text because there is a newline after 'Ganelin', so the final \\, preceding the#end is
part of the body of the block.

If $jazz is false, the output is

\

Note that things start to break if script elements are not properly escaped.

\\\#if( $jazz )
    Vyacheslave Ganelin
\\#end

Here the #if is escaped, but there is an #end remaining; having too many endings will cause a parsing error.

VTL: Formatting Issues

Although VTL in this user guide is often displayed with newlines and whitespaces, the VTL shown below

#set( $imperial = ["Munetaka","Koreyasu","Hisakira","Morikune"] )
#foreach( $shogun in $imperial )
    $shogun
#end

is equally valid as the following snippet that Geir Magnusson Jr. posted to the Velocity user mailing list to illustrate a completely unrelated point:

Send me #set($foo=["$10 and ","a pie"])#foreach($a in $foo)$a#end please.

Velocity's behaviour is to gobble up excess whitespace. The preceding directive can be written as:

Send me
#set( $foo = ["$10 and ","a pie"] )
#foreach( $a in $foo )
$a
#end
please.

or as

Send me
#set($foo       = ["$10 and ","a pie"])
                 #foreach           ($a in $foo )$a
         #end please.

In each case the output will be the same.

Other Features and Miscellany

Math

Velocity has a handful of built-in mathematical functions that can be used in templates with the set directive. The following equations are examples of addition, subtraction, multiplication and division, respectively:

#set( $foo = $bar + 3 )
#set( $foo = $bar - 4 )
#set( $foo = $bar * 6 )
#set( $foo = $bar / 2 )

When a division operation is performed between two integers, the result will be an integer, as the fractional portion is discarded. Any remainder can be obtained by using the modulus (%) operator.

#set( $foo = $bar % 5 )

Range Operator

The range operator can be used in conjunction with #set and #foreach statements. Useful for its ability to produce an object array containing integers, the range operator has the following construction:

[n..m]

Both n and m must either be or produce integers. Whether m is greater than or less than n will not matter; in this case the range will simply count down. Examples showing the use of the range
operator as provided below:

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]

Produces the following output:

First example:
1 2 3 4 5

Second example:
2 1 0 -1 -2

Third example:
0 1

Fourth example:
[1..3]

Note that the range operator only produces the array when used in conjunction with #set and #foreach directives, as demonstrated in the fourth example.

Web page designers concerned with making tables a standard size, but where some will not have enough data to fill the table, will find the range operator particularly useful.

Advanced Issues: Escaping and !

When a reference is silenced with the ! character and the ! character preceded by an \ escape character, the reference is handled in a special way. Note the differences between regular escaping, and the
special case where \ precedes ! follows it:

#set( $foo = "bar" )
$\!foo
$\!{foo}
$\\!foo
$\\\!foo

This renders as:

$!foo
$!{foo}
$\!foo
$\\!foo

Contrast this with regular escaping, where \ precedes $:

\$foo
\$!foo
\$!{foo}
\\$!{foo}

This renders as:

$foo
$!foo
$!{foo}
\bar

Velocimacro Miscellany

This section is a mini-FAQ on topics relating to Velocimacros. This section will change over time, so it's worth checking for new information from time to time.

Note : Throughout this section, 'Velocimacro' will commonly be abbreviated as 'VM'.

Can I use a directive or another VM as an argument to a VM?

Example : #center( #bold("hello") )

No. A directive isn't a valid argument to a directive, and for most practical purposes, a VM is a directive.

However..., there are things you can do. One easy solution is to take advantage of the fact that 'doublequote' (") renders its contents. So you could do something like

#set($stuff = "#bold('hello')" )
#center( $stuff )

You can save a step...

#center( "#bold( 'hello' )" )

Please note that in the latter example the arg is evaluated inside the VM, not at the calling level. In other words, the argument to the VM is passed in in its entirety and evaluated within the VM it was passed into. This
allows you to do things like :

#macro( inner $foo )
  inner : $foo
#end

#macro( outer $foo )
   #set($bar = "outerlala")
   outer : $foo
#end

#set($bar = 'calltimelala')
#outer( "#inner($bar)" )

Where the output is

Outer : inner : outerlala

because the evaluation of the "#inner($bar)" happens inside #outer(), so the $bar value set inside #outer() is the one that's used.

This is an intentional and jealously guarded feature - args are passed 'by name' into VMs, so you can hand VMs things like stateful references such as

#macro( foo $color )
  <tr bgcolor=$color><td>Hi</td></tr>
  <tr bgcolor=$color><td>There</td></tr>
#end

#foo( $bar.rowColor() )

And have rowColor() called repeatedly, rather than just once. To avoid that, invoke the method outside of the VM, and pass the value into the VM.

#set($color = $bar.rowColor())
#foo( $color )

Can I register Velocimacros via #parse() ?

Yes! This became possible in Velocity 1.6.

If you are using an earlier version, your Velocimacros must be defined before they are first used in a template. This means that your #macro() declarations should come before using the Velocimacros.

This is important to remember if you try to #parse() a template containing inline #macro() directives. Because the #parse() happens at runtime, and the parser decides if a VM-looking element in the template is a VM at parsetime,
#parse()-ing a set of VM declarations won't work as expected. To get around this, simply use thevelocimacro.library facility to have Velocity load your VMs at startup.

What is Velocimacro Autoreloading?

There is a property, meant to be used in development, not production :

velocimacro.library.autoreload

which defaults to false. When set to true along with

<type>.resource.loader.cache = false

(where <type> is the name of the resource loader that you are using, such as 'file') then the Velocity engine will automatically reload changes to your Velocimacro library files when you make them, so you do not have to dump the
servlet engine (or application) or do other tricks to have your Velocimacros reloaded.

Here is what a simple set of configuration properties would look like.

    file.resource.loader.path = templates
    file.resource.loader.cache = false
    velocimacro.library.autoreload = true    

Don't keep this on in production.

String Concatenation

A common question that developers ask is How do I do String concatenation? Is there any analogue to the '+' operator in Java?.

To do concatenation of references in VTL, you just have to 'put them together'. The context of where you want to put them together does matter, so we will illustrate with some examples.

In the regular 'schmoo' of a template (when you are mixing it in with regular content) :

       #set( $size = "Big" )
       #set( $name = "Ben" )

      The clock is $size$name.   

and the output will render as 'The clock is BigBen'. For more interesting cases, such as when you want to concatenate strings to pass to a method, or to set a new reference, just do

      #set( $size = "Big" )
      #set( $name = "Ben" )

      #set($clock = "$size$name" )

      The clock is $clock.    

Which will result in the same output. As a final example, when you want to mix in 'static' strings with your references, you may need to use 'formal references' :

      #set( $size = "Big" )
      #set( $name = "Ben" )

      #set($clock = "${size}Tall$name" )

      The clock is $clock.    

Now the output is 'The clock is BigTallBen'. The formal notation is needed so the parser knows you mean to use the reference '$size' versus '$sizeTall' which it would if the '{}' weren't there.

Feedback

如果在本手册中,你碰到任何错误,或者有其它关于Velocity用户手册的建议,请发邮件给Velocity
developers list
.谢谢!

抱歉!评论已关闭.