diandian的gravatar头像
diandian 2017-06-21 23:18:49
velocity翻译--用户指南[结束]

##关于本指南
用户指南意在帮助页面设计者和内容提供者熟识Velocity和它简单但强大的脚本语言(VTL)。这篇指南中许多例子使用Velocity在web网站中动态嵌入内容,但所有的VTL例子同样适用于其他页面和模板。
非常感谢选择Velocity!

##什么是Velocity?
Velocity是Java基础模板引擎。它允许web网页设计者参照java编码中定义的函数方法。Web设计者可以基于MVC模型平行开发web网站,这意味着web页面设计者可以集中精力于创建一个更好的设计的网站,编码者可以集中精力于编写一流的代码。Velocity将Java代码和web网页分离开,使web网站从长远来看更易于维护,同时为JSP或php提供了一种可行的替代。

Velocity可以被用于普通的web网页,SQL,PostScript和其他模板的输出。它可以被用于单独的实用程序来生成源码和报告,或者作为其它系统的集成插件。当完成后,Velocity会为Turbine web应用框架提供模板服务。
Velocity+Turbine会提供一种模板服务,允许web应用程序基于真正的MVC模型来发展。

##Velocity可以为我做什么?
###Mud 商店实例
假如你是一个专门在线卖mud在线商店的网页设计者。让我们暂且叫它“在线mud商店”。业务是蒸蒸日上的。顾客为各种各样或各种数量的mud下订单。他们用自己的用户名和密码登录你的网站,可以查看他们的订单或买更多的mud.现在Terracotta Mud降价销售,这非常流行。一部分你的顾客常规的买明亮的红色的Mud,这种Mud同样也在降价销售,尽管不受欢迎,通常放在你web页面的边缘。每位顾客的信息都在你的数据库中可以追踪,正因如此,一天问题升级了,为什么使用Velocity来定位对那些类型的泥感兴趣的顾客呢?

Velocity使用户在线访问web页面变得更加简单。作为一个在mud room的网站设计者,你想要使网页在用户登录你的网站后就能看到。

你在公司里和软件工程师见面,所有人都同意$customer将获取用户当前登录的信息,$mudsOnSpecial会成为目前促销的所有类型的mud。$flogger对象包含帮助促销的所有的方法。对于手头的任务来说,我们只需关注这三个引用。记住,你不需要担心软件工程师如何从数据库中获取数据,你只需要知道这行得通。这样你继续你的工作,软件工程师继续他们的工作。

你可以嵌入以下的VTL清单在web页面中:
```
<html>
  <body>
    Hello $customer.Name!
    <table>
    #foreach( $mud in $mudsOnSpecial )
      #if ( $customer.hasPurchased($mud) )
        <tr>
          <td>
            $flogger.getPromo( $mud )
          </td>
        </tr>
      #end
    #end
    </table>
  </body>
</html>
```
foreach表述的准确详情在后续会被介绍;重要的是这个简短的脚本在你的网站上的影响。当爱好明亮的红色的Mud的用户登录系统后,明亮的红色的Mud正在降价销售,客户将会看到这样的页面,突出显示的。如果另一个有着对明亮的红色的Mud购买记录的客户登录系统,Terracotta Mud正在销售的消息会被放在网站正前方。Velocity的灵活性是强大的,只受限于你的创造力。

记录在VTL参考中有很多其他的Velocity元素,这些元素一起赋予了网站存在的力量和灵活性。当你越来越熟悉这些元素,你就会开始解放Velocity的能力。

###Velocity 模板语言(VTL):简介
Velocity模板语言(VTL)旨在提供最早的,最简洁最清晰的方式在web网页中一体化动态内容。即使网页开发者有一些甚至是没有编码经验也会在短期内可以使用VTL在网站中一体化动态内容。

VTL在网站中使用引用来嵌入动态内容,变量就是一种类型的引用。变量是可以参考Java代码的定义的一种类型的引用,可以在web网页中从VTL表达式中获取到它自己的值。这是VTL表达式的一个例子可以嵌入在HTML中:
```
    #set($a = "Velocity")
```

这个VTL表达式,如同所有VTL表达式,以#开头同时包含set指令。当在线用户访问你的网页时,Velocity模板引擎会通过你的网页发现所有#字符,然后决定哪一个标识VTL指令的开始,哪一个#字符与VTL没有关系。

"#"字符后跟随着set指令。set指令使用一种语法(以上括号中的范例)--将一个值赋给一个变量。变量在左边,值在右边;这两个被“=”分隔。

在以上范例中,变量是$a,值是Veloctiy。这个变量,像所有引用一样,以$开头。字符串类型的值通常被包含在括号中,无论是单引号还是双引号。单引号会确保引用的值被赋予上。双引号允许使用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!”.
为了使包含VTL引用命令更有可读性,我们建议每个VTL表达式另起一行,及时你不必须这样做。set指令会在更多详情中再次访问。

###注释【Comments】
注释允许模板引擎引入没有输出的描述性文字。注释是一种有用的方式来提醒你自己同时解释给其他人,你的VTL表达式正在做什么,或者任何其他的你发现的有用的意图。下面是一个VTL的comment的例子。
 ``
  ##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.

还有第三类注释,VTL注释块,可以被用于保存任何类型的你想在模型中追踪的额外信息(例如javadoc类型的作者和版权信息)

    #**
      This is a VTL comment block and
      may be used to store such information
      as the document author and versioning
      information:
        @author John Doe
        @version 5
    *#

###引用
在VTL中有三种类型的引用:变量,属性和函数。作为使用VTL的设计者,你和你的工程师们必须对引用的特殊名称达成一致的协议以便可以在你的模型中正确的使用它们。

###变量
变量的标识由$字符开头,跟上一个VTL标识。
VTL标识必须以字母(a..z或者A..Z)开头。
其他符号由以下字符类型构成:

    字母(a~z或A~Z)
    数字(0~9)
    连字符("-")
    下划线("_")
这里是VTL中一些有效的变量范例:

    $foo
    $mudSlinger
    $mud-slinger
    $mud_slinger
    $mudSlinger1
当VTL代表一个变量,例如$foo,这个变量可以从模板的set指令获取值,也可以从Java代码中获取到值。例如,如果Java变量$foo在模板请求时有自己的值bar,bar将会替代web页面中的所有实例。或者,如果我引入了以下表达式
    
    #set($foo = "bar")
在这个指令之后所有的$foo的输出值都会相同。

###属性
VTL第二个风趣的引用是属性,属性有自己的不同的格式。
属性以$开头,"."跟随其后,随后是VTL标识。
这些是在VTL中有效的属性引用范例:
    
    $customer.Address
    $purchase.Total
拿第一个例子来说,$customer.Address可以有两种含义。可以表示在hashtable中标记一个customer的key同时返回这个key地址对应的值。但$customer.Address同样也可以表示一个方法(引用方法会在以下部分来讨论);$customer.Address可以作为一种$customer.getAddress()的缩写。当页面被请求,Velocity会决定这两种可能的方式哪种有意义,然后返回合适的值。

###方法
在Java代码中定义的方法可以做些有用的,例如计算或形成一个决定。方法引用以$开头,跟随一个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"] )
    ## 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**

在Velocity1.6版本中,所有数组引用被引用为固定长度的集合。这意味着可以用数组引用来调用java.util.List。所以,如果有一个数组引用(比如我们定义一个有三个值的字符串数组),可以这样做:

    $myarray.isEmpty()
    $myarray.size()
    $myarray.get(2)
    $myarray.set(1,'test')
同样1.6版本的Velocity支持可变参数的函数。一个函数像azpublic void setPlanets(String... planets)或者只是public void setPlanets(String[] planets)(如果你使用Java之前的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()

###渲染
每一个引用(无论是变量,属性或者函数)最终的结果都会转化成一个字符串对象渲染输出。如果有一个对象代表$foo(例如一个Integer类型的对象),那么Velocity会调用它的toString()方法来解析对象为字符串类型。

###索引注解
$foo[0]这种形式使用索引可以被用于给对象赋予一个索引。这种形式与调用这个被赋值对象的get(Object)方法类似,例如$foo.get(0),同时为一些操作提供了本质上的一个语法简写。由于只是简单的调用get方法,以下所有的方式都是有效的引用:

    $foo[0]       ## $foo使用一个Integer类型查阅
    $foo[$i]      ## 使用另一个引用作为索引  
    $foo["bar"]   ## 传参是一个字符串类型,$foo可能是个Map
以上括号中的语法同样作用于Java数组中,因为Velocity数组访问对象提供了一个get(Integer)方法返回指定的元素。
括号中的语法在任何地方都是有效的,例如:

    $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,而$vice是你想要使用的标识符。找不到任何$vicemaniac对应的值,Velocity会返回$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版本介绍了严格引用模式的理念为使velocity的配置属性‘runtime.reference.strict’实现动态使用。这一配置的意图是使Velocity更严谨以防未定义或引起企业,使程序语言更简单化,使Velocity的一些使用可能变得更加适用。例如如果未定义或者产生歧义会抛出异常。以下列出的内容是讨论严格引用模式和传统模式的不同。

setting引用不仅在内容中放置明确,而且应用在#set指令的定义或者Velocity会抛出异常时。在内容中使用空值不会抛出异常。除此之外,在引用对象中的尚未定义详细的方法或者未定义的一个属性时,Velocity会抛出一个异常。同样,在空值上调用方法或属性同样会抛出异常。

在以下例子中$bar被定义了,但$foo没有被定义,以下所有的表述都会抛出异常:

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

同样,以下范例展示了当调用不存在的方法或者属性时Velocity会抛出异常。在这些例子中,$bar包含一个定义了会返回字符串的foo属性和返回空值的retnull属性。

    $bar.bogus          ## $bar does not provide property bogus, Exception
    $bar.foo.bogus      ## $bar.foo does not provide property bogus, Exception
    $bar.retnull.bogus  ## cannot call a property on null, Exception</pre>

严格引用操作适用于除了在#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尝试渲染但空值会引起抛异常。如果简单的要渲染nothing可以在引用之前使用$!代替$,和非严格引用模式很类似。注意如果在严格引用模式中试图渲染一个内容中不存在的引用还是会经常抛异常的。例如,以下的$foo在上下文中有一个空值

    this is $foo    ## throws an exception because $foo is null
    this is $!foo   ## renders to "this is " without an exception
    this is $!bogus ## bogus is not in the context so throws an exception

###替代案例
现在你对于引用比较熟悉了,你可以在模板中有效的应用它们。模板引擎设计者发现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的自省和bean的特性来解决在上下文和对象方法中的引用名称。在模板中可能在任何地方嵌入或评价引用。

Velocity,在Bean说明文档中被微软定义为是区分大小写的。然而,开发者可能在任何可能的位置捕获并纠正用户的错误。当getFoo()方法在模板中被$bar.foo引用时,Velocity会首先尝试$getfoo。如果失败,会再尝试$getFoo.类似的,当模板中涉及到$bar.Foo时,Velocity会首先尝试$getFoo()然后尝试getfoo().

注意:在模板中引用变量实例问题仍没有解决。只有在JavaBean中的getter或setter方法的属性等价物等是被实现了的(例如$foo.Name被解析为class Foo的getName()方法,而不是Foo中的公共变量Name实例)。

###指令
引用允许模板设计者在web网站中生成动态内容,当指令--简单的使用脚本来创造性的处理Java编码的输出--允许网站设计者真正的负责网站的外观和内容。

指令通常以一个#开头。例如引用,指令的名字可能会被{}括起来。在指令后立刻跟着文本是很有用的一种方式。例如以下代码产生了一个错误:

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

等号左边的(LHS)一定要是个变量的引用或者一个属性的引用。等号右边(RHS)可以是以下的任一类型:

变量的引用
字符串
属性的引用
方法的引用
数值
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返回相同的值。

RHS同样可以有类似简单的语法:

    #set( $value = $foo + 1 )
    #set( $value = $bar - 1 )
    #set( $value = $foo * $bar )
    #set( $value = $foo / $bar )
如果RHS是一个属性或者方法的引用,且判定为null值,那么它就不会被赋值给左边的元素。取决于Velocity如何配置,通常不太可能通过这个途径从上下文中移除一个已存在的引用。(注意这可以通过改变Velocity的配置属性来操作)。这部分对于Velocity新手来说可能会困惑。例如:

    #set($result = $query.criteria("name"))
    第一个查询的结果是$result

    #set($result = $query.criteria("address"))
    第二个查询的结果是$result

如果$query.criteria("name")返回字符串“bill”,$query.criteria("address")返回的是空值,以上的VTL会趋向于以下的结果:

    第一个查询的结果是bill
    第二个查询的结果是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(添加到文本中),可能会是空值(从文本中移除掉)。#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
不同于其他的一些指令,#set指令没有#end结尾。
###字面值【Literals】
当使用#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

可替代的,#[[不转换文本]]]语法允许模板设计者在VTL编码中可以简单使用大块不解释的不转换的文本。这会在绕过大量指令或在某些无效的VTL或无需转换的VTL语句中尤为有用。

    #[[
    #foreach ($woogie in $boogie)
      nothing will happen to $woogie
    #end
    ]]#

渲染后的效果是:

    #foreach ($woogie in $boogie)
      nothing will happen to $woogie
    #end

###条件语句
If/ElseIf/Else
在Velocity中#if指令表示在条件满足true的情况下,允许网页生成后被加载。例如:
    #if( $foo )
      <strong>Velocity!</strong>
    #end
变量$foo被判定是否为true,这会在以下几种环境中出现:

$foo是一个为true的boolean值(true/false)
$foo是一个字符串或一个集合,都不是空值且不为空
$foo是一个为0的数值
$foo是一个不为空值的对象(而不是字符串,数值或者集合)

(请注意这是默认的行为,但Velocity可以配置跳过所有布尔和无效值的检查)

请记住Velocity上下文内容只包含对象,所以当我们说'boolean',它会解析为一个Boolean对象类。这同样适用于方法返回boolean-自检基础设施会返回一个Boolean对象的逻辑值。

如果#if的语句评定为true,那么#if和#end之间的表述则会被输出。在这种情况下,如果$foo为true,输出会是:"Velocity!".相反的,如果$foo是个空值,或是个为false的布尔值,判断表述为false,这里将不会有任何输出。

一个#elseif或者#else元素可以和#if元素同时使用。注意Velocity模板引擎会在发现第一个为true的表达式时停下来。在一下例子中,假设$foo有一个15的值,$bar有一个为6的值。

    #if( $foo < 10 )
        **Go North**
    #elseif( $foo == 10 )
        **Go East**
    #elseif( $bar == 6 )
        **Go South**
    #else
        **Go West**
    #end
在这个例子中,$foo大于10,所以前两个比较语句是失败的。下一个$bar和6比较,那个是真,所以输出为Go South.

###关系和逻辑运算符

Velocity使用等价的符号来比较变量之间的关系。这里是一个简单的例子来阐述等价操作符是如何使用的。

    #set ($foo = "deoxyribonucleic 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

注意==的语义和Java的仅可用于比较对象相等性的==是有轻微不同的。在Velocity中,等号可用于直接和数值、字符串或对象做比较。当两个不同类的对象做比较,字符串表达式会获取每个对象的toString()方法然后进行比较。

Velocity也有逻辑运算符AND,OR和NOT。以下的例子展示了逻辑运算符的使用方法。

    ##逻辑运算符 AND
    
    #if( $foo && $bar )
      ** This AND that**
    #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 )
        **This OR That**
    #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 )
      **NOT that**
    #end
这里,如果$foo为true,那么!$foo为false,这里没有任何输出。如果$foo为false,那么!$foo判定为true,“NOT that”会被输出。注意不要混淆$!foo和安静引用$!foo。

所有的逻辑符号都有对应的文本版本,包括eq,ne,and,or,not,gt,ge,lt和le.

注意更有用的一点。当你希望在#else指令后立刻包含文本,你会需要使用大括号来包含指令来区分其后跟随的文本。(任何指令都可以被大括号限定,虽然对#else指令会更有用些)。

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

###循环语句
Foreach循环

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

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

.#foreach循环使$allProducts列表(对象)可以用于在所有的products(目标)列表中循环。每次通过循环,$allProducts的值都存入$product变量中。

$allProducts变量的内容是一个Vector,一个Hashtable或者一个数组。赋值在$product变量上的值是一个Java对象,可以从一个变量中被引用。例如,如果$product真的是一个Java中的Product类,它的名字可以被$product.Name引用检索到.

让我们定义$allProducts是一个Hashtable.如果你想检索这个Hashtable中的key values如再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循环的索引,你可以使用$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指令在任何时间停止:

    #只取list的前五个顾客

    #foreach( $customer in $customerList )
        #if( $foreach.count > 5 )
            #break
        #end
        $customer.Name
    #end

###包含
.#include脚本元素允许模板设计者引入一个本地文件,然后引入在#include指令定义的位置中。文件的内容不会通过模板引擎渲染过来。出于安全考虑,被引入的文件可能只保存在TEMPLATE_ROOT目录下。

    #include("one.txt")
.#include指令涉及到的是引号中的文件。如果不止一个文件会被引入尽量,那么它们应该被逗号分隔。

    #include("one.gif","two.txt","three.htm")
被引入的文件需要不被命名的情景;事实上,引入更偏向于使用一个变量而不是一个文件名。当网页请求被提交后,这可能更加有用来输出基于标准的决定。这里是一个例子来同时展示一个文件名和一个变量。

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

###转换
.#parse脚本元素允许模板设计者导入包含VTL语言的本地文件。Velocity会转换VTL语言并渲染到指定的模板上。
    
    #parse("me.vm")
像#include指令,#parse可以获取一个变量而不仅仅是一个模板。任意模板要被引入#parse指令中必须被引入到TEMPLATE_ROOT下。不像#include指令,#parse只获取一个参数。

VTL模板中#parse应用的文件能够嵌套引用包含#parse的模板。默认为10,用户可以通过velocity.properties的directive.parse.max.depth这一行属性来定制转换参考数据。(注意:如果directive.parse.max.depth属性在velocity.properties中没有定义,Velocity会默认为10)递归是允许的,例如,如果dofoo.vm属性包含以下的这些代码行:

    Count down.
    #set( $count = 8 )
    #parse( "parsefoo.vm" )
    所有操作均在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从8开始调用parsefoo.vm。
当count变量为0时,会显示“All done with parsefoo.vm!”。在这时候,Velocity会返回到dofoo.vm上输出“All done with parsefoo.vm!”消息。
###Break
.#break指令停止了在目前执行范围内的将要渲染的操作。一个执行范围本质上是内容上的任何指令(例如#foreach,#parse,#evaluate,#define,#macro, or #@somebodymacro)或者任一root范围(例如template.merg(...),Velocity.evaluate(...)或者Velocity.evaluate(...))。不像#stop,#break会只停止内层循环,当前循环范围的,而不是所有循环。

如果希望跳出特定的执行范围,此执行范围不是立即需要执行的,那么你可以通过范围控制参考(例如$foreach, $template, $evaluate, $define, $macro或者 $somebodymacro)作为#break的一个参数。(例如#break($macro)).这会停止所有循环范围内指定的循环。当同一类型的嵌套循环,记住你总可以使用parent(s)通过$.parent或者$.topmost,通过这些来#break,而不是(例如#break($foreach.parent)或者#break($macro.topmost)).
###Stop
.#stop指令可以中断模板中任意执行的循环。即使当指令被其他模板通过#parse指令嵌套或加载到一个velocity的宏定义中使用,stop也是有效的。这样导致最终所有内容都几种在#stop指令上输出。这在模板中是很遍历的出口。出于debug的目的,你可能会在实现stop命令之前提供一个消息参数(例如#stop('$foo was not in context') )写入logs(当然是DEBUG级别)
###Evaluate
.#evaluate指令用于动态获取VTL的值。允许模板在渲染时求字符串的值。例如一个字符串可能被用于国际化模板或从数据库中引入模板的部分数据。

以下的例子会显示abc

    #set($source1 = "abc")
    #set($select = "1")
    #set($dynamicsource = "$source$select")
    ## $dynamicsource is now the string '$source1'
    #evaluate($dynamicsource)
###Define【定义】

.#define指令可以引入VTL块做参考。
以下范例会显示Hello World!

    #define($block)Hello $who#end
    #set($who = 'World!')
    $block
###    Velocimacros【宏调用】
.#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渲染到body上。
    #@d()Hello!#end
你可以像以前一样调用宏,同时因为我们为body引用使用安静引用符号($!bodyContent代替$bodyContent),它仍会渲染一行空的数据列。

一个宏定义也可以获取许多参数--甚至没有参数,例如在第一个例子中论证的,是一个观点--但当宏定义被提及,它必须被以相同数量定义的参数来调用。很多宏定义比以上案例中定义的更复杂。这里是一个有两个参数的宏定义,一个color和一个数组。

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

宏定义在这个例子中被定义,tablerows,有两个参数。第一个参数由$color代替,第二个参数由$somelist代替。

任意可以放入VTL模板中的元素都可以放入宏定义的方法体内。以上tablerows宏定义是一个foreach表达式。有两个#end表达式被定义在#tablerows宏定义中;第一个属于#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模板中内嵌定义,意味着他在同一网站别的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会在模板库中发现#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>

###宏定义参数
宏定义可以获取以下这些VTL元素中任一类型的参数:

参考:任何以$开头的
字符串字面值:例如"$foo"或'hello'
数值字面值:1,2等等
数值范围:[ 1..2] or [$foo .. $bar]
对象数组: [ "a", "b", "c"]
布尔类型的true
布尔类型的false


打赏
最近浏览
遇见,  LV36 2017年11月25日
diandian  LV8 2017年6月23日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友