Velocity User Guide 用戶手冊

轉自: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.謝謝!




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章