asp.net模板引擎:Velocity用户手册



Velocity用户手册是帮助页面设计者和内容提供者认识Velocity和其简单而功能强大的脚本语言――Velocity模板语言(VTL)。在手册上的许多例子,都是用Velocity插入动态的内容到网页上,但是所有的VLT例子都能应用到其他的页面和模板中。

Velocity用户手册是帮助页面设计者和内容提供者认识Velocity和其简单而功能强大的脚本语言――Velocity模板语言(VTL)。在手册上的许多例子,都是用Velocity插入动态的内容到网页上,但是所有的VLT例子都能应用到其他的页面和模板中。

感谢使用 Velocity!

Velocity是什么?

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。

Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocityjava代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在JSPPHP之外又提供了一种可选的方案。

Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQLPostScriptXML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。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声明:

<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 的详细用法不久就会进行深入描述。重要的是,这短小的脚本能在你的网站上出现。当一个对Bright Red Mud 很感兴趣的顾客登陆的时候,同时Bright Red Mud在热卖中,这时顾客就能显著地看到。假如一个玩Terracotta Mud很久的顾客登陆,Terracotta Mud 的售卖信息就会出现在前面中间。Velocity的适用性是很巨大的,限制的只是你的创造性。

    VTL Reference 含有许多其他Velocity元素,这些元素能够共同帮助你,使你的网站更加好。当你越来越熟悉那些原理,你开始释放Velocity的能力。

Velocity模板语言(VTL):说明

VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。VTL使用references来在web site内嵌套动态内容,一个变量就是一种类型的reference。变量是某种类型的refreence,它可以指向java代码中的定义,或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子,它可以被嵌套到HTML代码中:

#set( $a ="Velocity" )

和所有的VTL statement一样,这个statement以#字符开始并且包含一个directiveset。当一个在线用户请求你的页面时,Velocity 模板引擎将查询整个页面以便发现所有#字符,然后确定哪些是VTL statement,哪些不需要VTL作任何事情。

#字符后紧跟一个directiveset时,这个set directive使用一个表达式(使用括号封闭)――一个方程式分配一个值给变量。变量被列在左边,而它的值被列在右边,最后他们之间使用=号分割。

在上面的例子中,变量是$a,而它的值是Velocity。和其他的references一样以$字符开始,而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。

使用$字符开始的references用于得到什么;使用#字符开始的directives用于作些什么。在上面的例子中,#set是分配一个值给变量。变量$a 在模板中输出 "Velocity"。

Hello VelocityWorld!

一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。

<html>

<body>

#set( $foo ="Velocity" )

Hello $foo World!

</body>

<html>

上面的实现结果是在页面上打印“Hello Velocity World

为了使包含VTL directivesstatement更具有可读性,我们鼓励你在新行开始每个VTL statement,尽管你不是必须这么作。Set的用法将在后面详细描述。

注释

        注释是那些描述文本不出现在模板引擎输出里面。注释一个主要用处是提醒自己和解释出现在VTL中的声明,或是其他用途。下面是一个在VLT中的注释例子。

## This is a singleline comment.

单行注释以##开始,结束在这行的结尾。如果你要写几行注释,这没有必要写几个单行注释。

多行注释,以#*开始*#结束,可以解决这个问题。

This is text thatis outside the multi-line comment.

Online visitors cansee it.

#*

Thus begins a multi-line comment. Onlinevisitors won't

see this text because the Velocity TemplatingEngine will

ignore it.

*#

Here is textoutside the multi-line comment; it is visible.

这里有几个例子关于单行和多行注释如何工作。

This text isvisible. ## This text is not.

This text isvisible.

This text isvisible. #* 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 itis visible.

## This text is notvisible.

这里有第三种类型的注释,VLT注释块,可能用于存放文档的作者名称和版本信息。

#**

This is a VTL comment block and

may be used tostore such information

as the documentauthor 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)开头。剩下的部分限于以下几种:

l  字母(a..z,A..Z)

l  数字(0..9)

l  连字符(“-”)

l  下划线(“_”)

这里是几个在VTL中有效的变量reference

$foo

$mudSlinger

$mud-slinger

$mud_slinger

$mudSlinger1

VLT定义一个变量,例如$foo,变量能或者通过模板中的set 方法,或者通过Java 代码获得值。

例如,Java 变量$foo 的值是bar ,在这个模板被请求时,在网页上bar 会替代所有$foo 。选择地,假如包括了下面的声明:

#set( $foo = "bar" )

按照这样的设置,输出就会跟之前的一样。

属性

第二种有趣的VTL reference就是属性,而且属性有一种与众不同的格式。非正式变量是由“$”开头,接着是VTL标识符,再接着就是字符(“.”)和其他的VLT标识符。这里是一些在VLT中有效属性的定义:

$customer.Address

$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" )

$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"] )

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

${purchase.getTotal()}

在几乎所有场合你都可以使用非正式references,但是在某些场合,只能使用正式reference 才能正确处理。

设想你创建一个句子:$vice 作为句子的名词 。目标是为了使某些人选择不同的词,产生下面两种结果之一:"Jack is a pyromaniac." 或者 "Jack is a kleptomaniac."。使用非正式定义不太适合用于这种情况。看一下下面的例子:

Jack is a $vicemaniac.

本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写

Jack is a ${vice}maniac.

现在Velocity就知道reference $vice,而不是$vicemaniac。正式定义经常用于模板中refernces与文本连接在一起的情况。

Quiet reference notation

Velocity遇到没有定义的reference,通常它会直接输出reference。例如:假如下面的reference出现在一个VTL模板中:

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

 form 最初加载的时候,变量$email 没有值,但你想出现一个空白的文本框设定值为“$email”。

使用quiet reference notation可以使Velocity正常显示,你需要用$!email ,代替$email。所以上面的例子,会改成下面:

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

这样文本框的初始值就不会是email而是空值了。

正式和quiet格式的reference notation也可一同使用,像下面这样:

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

Getting literal

Velocity使用特殊字符$#来帮助它工作,所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。

货币字符

这是没有问题的:"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 = $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.FooVelocity会先尝试 $getFoo(),然后再尝试 $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)可以是下面中的一个类型:

l  变量

l  字符串

l  属性

l  方法

l  数字

l  数组

这些例子显示上述的每一种类型:

#set( $monkey = $bill ) ## variablereference

#set( $monkey.Friend = "monica" ) ## stringliteral

#set( $monkey.Blame = $whitehouse.Leak ) ## propertyreference

#set( $monkey.Plan = $spindoctor.weave($web) ) ## methodreference

#set( $monkey.Number = 123 ) ##numberliteral

#set( $monkey.Say = ["Not", $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是一个nullVTL的处理将比较特殊:它将指向一个已经存在的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$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

在上面的例子中,程序将不能智能的根据$result的值决定查询是否成功。在$result#set后(added to the context),它不能被设置回nullremoved from thecontext)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。

为了解决以上问题我们可以通过预先定义的方式:

#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 directiveString literal封闭在一对双引号内。象下面:

#set( $directoryRoot = "www" )

#set( $templateName = "index.vm" )

#set( $template ="$directoryRoot/$templateName" )

$template

上面这段代码的输出结果为:

www/index.vm

但是,当string literal被封装在单引号内时,它将不被解析:

#set( $foo = "bar" )

$foo

#set( $blargh = '$foo' )

$blargh

输出为:

bar $foo

上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate= false的值来改变上面的特性是否有效。

条件语句

If / ElseIf / Else

当一个web页面被生成时使用Velocity#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:

#if( $foo )

 <strong>Velocity!</strong>

#end

上例中的条件语句将在以下两种条件下成立:(i)$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值的表达式就会停止。在下面的例子,假设 $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

在这个例子中,$foo10大,所以在开始的两个比较中都失败。接着 $bar6比较是真的所以输出为 GO South

注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,但是与java不同它使用==来比较两个值,而且velocity要求等号两边的值类型相同。

关系、逻辑运算符

Velocity中使用等号操作符判断两个变量的关系。这里有个简单例子关于等于号的使用:

#set ($foo = "deoxyribonucleic acid")

#set ($bar = "ribonucleic acid")

#if ($foo == $bar)

 In this case it'sclear they aren't equivalent. So...

#else

 They are notequivalent and this will be the output.

#end

VelocityANDORNOT逻辑运算符。想得到更多信息,请看VTLReference Guide。下面的例子是说明ANDORNOT逻辑运算符的用法:

## logical AND

#if( $foo && $bar )

 <strong> This AND that</strong>

#end

只有当$foo  $bar 都为true#if 才会得到true值。如果$foofalse ,表达式的值为 false$bar 就不会求值。如果 $foo的值为 true Velocity模板引擎会检查 $bar的值,如果$bar=true,整个表达式的值为true,输出为“This AND that”。如果$bar=false,整个表达式的值为false,没有输出。

逻辑OR 的工作方式一样,除了只要有一个值为true ,整个表达式的值就为true。考虑一下下面的例子。

## logical OR

#if( $foo || $bar )

  <strong>This OR That</strong>

#end

如果$footrueVelocity模板引擎就没有必要查找 $bar,无论 $bar true 还是 false ,表达式的值为true ,输出为“This OR That”。如果$fooflase$bar的值就一定要检查,在这个例子中,如果 $bar 同样是false,表达式的值为 false ,没有输出。从另外一个角度看,如果$bar 的值为true ,整个表达式的值为 true ,输出为 This OR That”。

关于逻辑NOT ,只有一个疑问:

##logical NOT

#if( !$foo )

<strong>NOT that</strong>

#end

如果 $foo=true, !$foo 的值为 false,没有输出。如果$foo=false!$foo的值为true,输出为“NOT that”。注意不要跟 quiet reference$!foo 混为一谈,那是完全不一样的。

循环

Foreach循环

#foreach 用于循环。例子:

<ul>

#foreach( $product in $allProducts )

  <li>$product</li>

#end

</ul>

每次循环$allProducts中的一个值都会赋给$product变量。

$allProducts可以是一个VectorHashtable或者Array。分配给$product的值是一个java对象,并且可以通过变量被引用。例如:如果$product是一个javaProduct类,并且这个产品的名字可以通过调用他的getName()方法得到。

现在我们假设$allProducts是一个Hashtable,如果你希望得到它的key应该像下面这样:

<ul>

#foreach( $key in $allProducts.keySet() )

  <li>Key: $key -> Value: $allProducts.get($key)</li>

#end

</ul>

Velocity还特别提供了得到循环次数的方法,以便你可以像下面这样作:

<table>

#foreach( $customer in $customerList )

  <tr><td>$velocityCount</td><td>$customer.Name</td></tr>

#end

</table>

$velocityCount变量的名字是Velocity默认的名字,你也可以通过修改velocity.properties文件来改变它。默认情况下,计数从“1”开始,但是你可以在velocity.properties设置它是从“1”还是从“0”开始。下面就是文件中的配置:

# Default name of the loop counter

# variable reference.

directive.foreach.counter.name = velocityCount

# Default starting value of the loop

# counter variable reference.

directive.foreach.counter.initial.value = 1

include

#include scriptelement允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因,被引入的本地文件只能在TEMPLATE_ROOT目录下。

#include( "one.txt" )

#include 引用的文件用引号括起来。

如果您需要引入多个文件,可以用逗号分隔就行:

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

在括号内可以是文件名,但是更多的时候是使用变量的。这用于根据页面提交的需求而输出。这里有一个例子同时有文件名和变量。

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

parse

#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTLrender模板。

#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 withparsefoo.vm!

#end

在显示“Count down”后,Velocity通过parsefoo.vm,从8往下数。当计数到了0,它就会显示“All done with parsefoo.vm!”。在这时,Velocity 会返回到dofoo.vm ,输出信息:“All done withdofoo.vm!”。

停止

#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于debug是很有帮助的。

#stop

Velocimacros

#macro script element允许模板设计者定义一段可重用的VTLtemplateVelocimacros广泛用于简单和复杂的行列。Velocimacros的出现是为了减少编码和极小化排版错误,对Velocimacros的概念提供一个介绍。

#macro( d )

<tr><td></td></tr>

#end

在上面的例子中Velocimacro被定义为d,然后你就可以在任何VTL directive中以如下方式调用它:

#d()

当你的template被调用时,Velocity将用<tr><td></td></tr>替换为#d()

每个Velocimacro可以拥有任意数量的参数――甚至0个参数,虽然定义时可以随意设置参数数量,但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro,一个参数是color另一个参数是array

#macro( tablerows $color $somelist )

#foreach( $something in $somelist )

  <tr><td bgcolor=$color>$something</td></tr>

#end

#end

Velocimacro在这个例子的定义,tablerows,有两个元素。第一个是替换$color ,第二个是替换$somelist

任何东西都可以通过Velocimacro加入到VTL模板中。tablerows Velocimacro 是一个foreach 标识。 这里有两个#end标识在#tablerowsVelocimacro 定义里面。第一个是属于 #foreach,第二个是属于Velocimacro定义的。

#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )

#set( $color = "blue" )

<table>

   #tablerows($color $greatlakes )

</table>

注意到$greatlakes 替代 $somelist。当#tablerowsVelocimacro被调用时,就会产生下面的输出:

<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模板内实现行内定义(inline),也就意味着同一个website内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的:它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。

上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(velocity.properties中定义)里,所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用:

#set( $parts =["volva","stipe","annulus","gills","pileus"])

#set( $cellbgcol = "#CC00FF" )

<table>

#tablerows( $cellbgcol $parts )

</table>

当为mushroom.vm实现一个请求时,Velocity会在模板库中找到#tablerowsVelocimacro(定义在velocity.properties文件),产生以下输出:

<table>

  <tr><tdbgcolor="#CC00FF">volva</td></tr>

  <tr><tdbgcolor="#CC00FF">stipe</td></tr>

  <tr><tdbgcolor="#CC00FF">annulus</td></tr>

  <tr><td bgcolor="#CC00FF">gills</td></tr>

  <tr><tdbgcolor="#CC00FF">pileus</td></tr>

</table>

Velocimacroarguments

Velocimacro可以使用以下任何元素作为参数:

l  Reference:任何以$开头的reference

l  String literal

l  Number literal

l  IntegerRange[1….3]或者[$foo….$bar]

l  对象数组:[“a”,”b”,”c”]

l  boolean值:truefalse

当将一个reference作为参数传递给Velocimacro时,请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro,而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如:

    #macro( callme$a )

       $a $a $a

    #end

    #callme($foo.bar() )

执行的结果是:reference $foobar()方法被执行了三次。

        在第一次扫描中,出现惊人的作用。但是当你考虑到Velocimacro的最原始动机:消除剪切、粘贴、复制,简单地使用VTL使它更灵活。它允许你对传递对象到Velocimacro,例如一个产生重复序列颜色,着色表格的行的对象。

如果你不需要这样的特性可以通过以下方法:

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

#callme( $myval )

Velocimacroproperties

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属性有两个可选值truefalse,这个属性允许使用者确定inlineVelocimacro定义是否可以替代全局Velocimacro定义(比如在velocimacro.library属性中指定的文件内定义的Velocimacro)。默认情况下,此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。

        Velocity.properties文件中的velocimacro.permissions.allow.inline.local.scale属性也是有truefalse两个可选值,默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说,如果这个属性设置为true,一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM窍门:a template can define a private implementation of thesecond VM that will be called by the first VM when invoked by that template.All other templates are unaffected

Velocity.properties文件中的velocimacro.context.localscope属性有truefalse两个可选值,默认值为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)。注意这个属性实际上是针对开发而非产品的。

VelocimacroTrivia

Velocimacro必须被定义在他们被使用之前。也就是说,你的#macro()声明应该出现在使用Velocimacros之前。

特别要注意的是,如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs

Escaping VTL directives

VTL directives can beescaped with “\”号,使用方式跟VTLreference使用逃逸符的格式差不多。

## #include( "a.txt" ) rendersas <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" )

在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心(比如在一个if-else-end statement内)。下面是VTLif-statement的典型应用:

#if( $jazz )

  Vyacheslav Ganelin

#end

如果$jazzture,输出将是:

Vyacheslav Ganelin

如果$jazzfalse,将没有输出。使用逃逸符将改变输出。考虑一下下面的情况:

\#if( $jazz )

  Vyacheslav Ganelin

\#end

现在无论$jazztrue还是false,输出结果都是:

#if($ jazz )

   Vyacheslav Ganelin

#end

事实上,由于你使用了逃逸符,$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的,例如:

\\#if( $jazz )

 Vyacheslav Ganelin

\\#end

以上程序的显示结果为:

\ Vyacheslav Ganelin

\

        为了方便理解,注意到#if(arg)当新一行的结束(return)在输出中会省略新行。因此,#if()块是接着第一个‘\’,显示来自在#if()前面的‘\\’。最后的‘\’在跟文本不同的行,因为那里有新的一行在‘Ganelin’后面,所以最后‘\\’在#end 的前面。

        但是如果$jazzfalse,那么将没有输出。注意:事情也有例外,如果scriptelements 没有完全的逃避。

\\\#if( $jazz )

  Vyacheslave Ganelin

\\#end

        在这里#if 已经逃避了,但是#end没有,有太多的#end 的产生解析错误。

VTLFormatting issues

尽管在此用户手册中VTL通常都开始一个新行,如下所示:

#set( $imperial =["Munetaka","Koreyasu","Hisakira","Morikune"])

#foreach( $shogun in $imperial )

  $shogun

#end

        但是像下面这种写法也是可以的:

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

        上面的代码可以被改写为:

Send me

#set( $foo = ["$10 and ","a cake"] )

#foreach( $a in $foo )

$a

#end

please.

或者

Send me

#set($foo       =["$10 and ","a cake"])

               #foreach           ($a in $foo )$a

        #endplease.

每一种的输出结果将一样。

其他特性和杂项

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作为输出结果。

RangeOperator

Range operator可以被用于与#set#foreach statement联合使用。对于处理一个整型数组它是很有用的,Range operator具有以下构造形式:

[n..m]

mn都必须是整型,而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 非常有用。

AdvancedIssue: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’。

1.       我能用一个标识或者另外的VM 作为一个VM的参数吗?

例如:#center ( #bold( “hello” ) )

答:不可以。一个directive的参数使用另外一个directive是不合法的。

但是,还是有些事情你可以作的。最简单的方式就是使用双引号,所以你可以这样做:

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

#center( $stuff )

        上面的格式也可以缩写为一行:

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

        请注意在下面的例子中参数被evaluatedVelocimacro内部,而不是在calling level。例子:

#macro( inner $foo )

 inner : $foo

#end

#macro( outer $foo )

  #set($bar ="outerlala")

  outer : $foo

#end

#set($bar = 'calltimelala')

#outer( "#inner($bar)" )

输出结果为:

Outer : inner : outerlala

        因为#inner($bar)的赋值发生在#outer(),所以$bar的值在#outer()赋给的。

记住Veloctiy的特性:参数的传递是By Name的。例如:

#macro( foo $color )

 <trbgcolor=$color><td>Hi</td></tr>

 <trbgcolor=$color><td>There</td></tr>

#end

#foo( $bar.rowColor() )

        以上代码将导致rowColor()方法两次调用,而不是一次。为了避免这种现象的出现,我们可以按照下面的方式执行:

#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

<type>.resource.loader.cache = false 连同一起设为true ()(<type>是你用的资源器,例如‘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.。使用这种格式主要是为了使得$size不被解释为$sizeTall

你可能感兴趣的:(asp.net模板引擎:Velocity用户手册)