前言
在參考官方的翻譯文檔時, 很多語句的翻譯的還有點生硬, 而且我在學的過程中有些地方讀起來很吃力, 我相信也會有跟我一樣的朋友, 所以寫此文, 希望把表達的不清楚的地方能稍微的解釋的變清楚一些, 同時加上一些自己對該語法的理解和看法, 當然, 不一定準確, 僅代表個人觀點.
set
#set( $a = "Velocity" )
這個VTL 語句, 像所有的VTL語句一樣,通過 # 字符開始并包含一個指令: set. 當一個用戶訪問你的頁面時,Velocity模板 將在你的Web頁面中搜索所有的#字符, 然后認為它是VTL語句的開始,但是#字符并沒有實際意義。
注釋
單行注釋
## 單行注釋
多行注釋
#*
多行注釋, Velocity會忽略此段
*#
多行文檔注釋
#**
這種注釋類似于javadoc
@author somebody
@version 1.7
*#
引用
vtl中引用分為三種, 變量, 屬性, 及方法
變量
當VTL應用一個變量時, 例如
$foo
,這個變量可以獲取一個值從模板的 set指令中, 或者從Java代碼中。例如,假如在Java中定義了一個變量foo
,java
中定義的值就是Web頁面中所有的$foo
引用.
或者, 我在頁面中定義下面語句
#set( $foo = "bar" )
,$foo
輸出的結果將和你定義的是一樣的。
屬性
VTL中第二個特點鮮明的引用是屬性引用, 屬性有一個與眾不同的格式. 它的標識符前面需要添加一個$變量標識符, 緊跟著后面一個點(“.”) . 下面是一個在VTL中屬性引用的實例:
$customer.Address
在vtl中, 我的理解, 它即可以是customer的屬性Address
, 也可以是customer.getAddress()
方法... 比el
表達式更強大, 你可以根據需求來返回你需要的值.
方法
方法跟java是一樣的, 也是可以傳參, 做很多事情
$customer.getAddress()
$purchase.getTotal()
$page.setTitle( "My Home Page" )
$person.setAttributes( ["Strange", "Weird", "Excited"] )
VTL方法和屬性的區別
VTL 屬性引用能夠被當著方法引用的簡寫. 屬性 $customer.Address 引用和 $customer.getAddress()方法的引用效果是一樣的. 一般情況下如果可以,我們通過簡寫的方式來引用方法.屬性和方法主要不同是方法能夠引用參數 .
下面的方法可以被屬性引用簡寫
$sun.getPlanets()
$annelid.getDirt()
$album.getPhoto()
但是下面的方法,就不可以簡寫
## 根據以下數組中參數獲取
$sun.getPlanet( ["Earth", "Mars", "Neptune"] )
## 添加一個fans
$user.addFans()
## 設定title
$book.setTitle( "Homage to Catalonia" )
假如你有一個引用在一個集合上 , 你就可以用集合對象的方法, 比如size,get(index),isEmpty等方法
$myarray.isEmpty()
$myarray.size()
$myarray.get(2)
$myarray.set(1, 'test')
支持可變參數
后臺對象中有一個setPhones(String... phones)的方法, vtl可以這樣用
$user.setPhones('13812345678', '15812345670', '18812345671')
$user.setPhones()
將會賦一個空數組
屬性調用規則
正如前面提到的, 屬性經常涉及到父類方法的引用. Velocity是十分擅長解決方法對應的屬性獲取,它可以根據幾種不同的命名約定進行選擇,準確的查找規則依賴是否屬性的名字以大寫開始。對于小寫名字,例如 $customer.address, 調用的順序是
getaddress()
getAddress()
get(“address”)
isAddress()
對于大寫的屬性名字像 $customer.Address, 它稍微不同:
getAddress()
getaddress()
get(“Address”)
isAddress()
渲染
每一個引用的值(變量,屬性,或者方法)都被轉換為一個字符串并作為最終的輸出。假如這里有一個對象表示為$foo (例如一個整數對象), 當Velocity調用它時,Velocity會調用它的.toString() 方法轉化為字符串.
索引標識符
$userList[2].name
等同于 $userList.get(2).name
一看就明白
$userMap["name"]
等同于userMap.get("name")
跟java代碼都是一樣的,非常方便
這相同的語法也能夠使用在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")
正式引用標識符 很重要
在大部分情況下你能夠使用標識符引用,但是有些情況下要求正確的符號被要求正確的處理。我認為, 應該盡量用
${}
來寫會比較好
一個簡單的例子:
Jack is a $vicemaniac.
Jack is a ${vice}maniac.
以上兩者的變量的引用是不一樣的, 這你就應該看懂了, 為什么用正式引用標識符比較好的原因.
靜態引用標識符
當Velocity遇到一個沒有定義的引用時,正常的是按照原文輸出的. 例如, 假如下面的引用是VTL模板的一部分.
<input type="text" name="email" value="${email}"/>
按照道理, 當email沒有值時, 我們希望的是value="", 但實際上vlt會在無值時輸出"$email".... 這就很尷尬了...相比el表達式就沒有這個問題對吧.
<input type="text" name="email" value="$!{email}"/>
這么寫, 就可以解決這個問題, 在$
后加上!
就好了..
模式替換或者說多種寫法
Velocity引用利用了一些Java的設計原則進行設計,很容易使用。 例如:
$foo.getBar()
## 等價于
$foo.Bar
$data.setUser("jon")
## 等價于
#set( $data.User = "jon" )
$data.getRequest().getServerName()
## 等價于
$data.Request.ServerName
## 等價于
${data.Request.ServerName}
指令
指令一直以 #開始.
#if ($userList.size() > 3)
用戶多于3人的情況
#else
用戶未達標
#end
模板會根據userList的長度, 輸出以上兩句話中的一句.
像引用一樣,指令的名字可能是相等的通過{ 和 } 符號. 這是好的方式
#{if} ($userList.size() > 3)
用戶多于3人的情況
#{else}
用戶未達標
#{end}
set指令
#set
指令被用來設定一個引用的值. 這個值能夠被分配一個變量引用或者屬性引用,這種情況發生在括號中, 如下實例:
#set( $primate = "monkey" )
#set( $customer.Behavior = $primate )
左邊的(LHS)必須分配一個變量引用或者屬性引用. 右邊的(RHS)可以是以下類型:
Variable reference
String literal
Property reference
Method reference
Number literal
ArrayList
Map
其實就跟java一樣, 就是賦值左邊要寫屬性, 右邊賦值可以是以上幾種類型, 下面是關于幾種類型賦值的寫法, 用時參考就好了:
#set( $monkey = $bill ) ## variable reference
#set( $monkey.Friend = "monica" ) ## string literal
#set( $monkey.Blame = $whitehouse.Leak ) ## property reference
#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference
#set( $monkey.Number = 123 ) ##number literal
#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList
#set( $monkey.Map = {"banana" : "good", "roast beef" : "bad"}) ## Map
同時右邊的值也能使用簡單的算術表達式, 這個跟el表達式也是一樣的道理:
#set( $value = $foo + 1 )
#set( $value = $bar - 1 )
#set( $value = $foo * $bar )
#set( $value = $foo / $bar )
另外 RHS如果是null值 則不會分配個LHS
#set( $result = $query.criteria("name") )
第一次輸出result 的值: $result
#set( $result = $query.criteria("address") )
第二次輸出result 的值: $result
如果$query.criteria("name") 的值是張三, 而$query.criteria("address")的值是null的話, 則輸出結果是:
第一次輸出result 的值: 張三
第二次輸出result 的值: 張三
也就是說, 第二次#set指令并沒有改變result的值.
再看下面的例子, 意圖應該:
#set( $monkey = {}) ## 初始化monkey
#set( $monkey.name = "猴子" )
##下面準備遍歷這個$monkey的key
#set ( $conditionKey = ["name", "food"])
#foreach ($key in $conditionKey)
#set ($result = $monkey[$key])
#if ($result)
輸出:該key有值
#end
#end
輸出結果應該是:
該key有值
該key有值
這個結果顯然不正確的, 因為$monkey.food 是沒有值的
如果要解決這個問題, 那么就需要給$result提前賦值一個false
#foreach ($key in $conditionKey)
#set ($result = false)
#set ($result = $monkey[$key])
#if ($result)
輸出:該key有值
#end
#end
這樣的話, 利用#set指令的特性, 不會賦null值, 則在key == food時, $result的結果依然是false
字面量, 字符串的操作
#set
指令的時候, 在雙引號里面的字符串將被解析, 如下所示:
#set( $directoryRoot = "www" )
#set( $templateName = "index.vm" )
#set( $template = "$directoryRoot/$templateName" )
$template
輸出結果:
www/index.vm
然而, 當字符串處在單引號中, 它將不被解析:
#set( $foo = "bar" )
$foo
#set( $blargh = '$foo' )
輸出結果:
bar
$foo
默認情況, 這種特征使用單引號不解析Velocity中的可用變量. 你也可以通過改變velocity.properties 中的stringliterals.interpolate=false配置來改變這種默認設置.
或者, #[[不要解析此段代碼]]# 語法準許模板設計者很容易的使用大量的語句塊,而這些語句塊中的變量不會被解析.
#[[
#foreach ($woogie in $boogie)
nothing will happen to $woogie
#end
]]#
所以, 以上這段代碼就會在頁面輸出
條件語句
If / ElseIf / Else
不管從語法, 還是邏輯運算符, 用法同java, 沒什么可說的
#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
這就是一組if else基本的寫法
關系和邏輯操作符
Velocity 使用等號決定兩個變量之間的關系. 下面簡單實例展示了等會的怎么使用.
#set ($foo = "deoxyribonucleic acid")
#set ($bar = "ribonucleic acid")
#if ($foo == $bar)
如果兩者相等輸出此處
#else
否則輸出此處
#end
邏輯操作符 AND, OR 和NOT 操作符.
vtl的邏輯操作符同java一樣, 并且也有短路的特性, 不懂的補習一下java的基礎知識
#if( $foo && $bar )
<strong> This AND that</strong>
#end
and邏輯操作符, 如果 $foo為false,表達式的結果為 false; $bar將不會計算. 這就是and短路特性
or邏輯操作符, 如果 $foo為true,表達式的結果為 true; $bar將不會計算. 這就是or短路特性
循環
#foreach元素用來循環操作
<ul>
#foreach( $product in $allProducts )
<li>$product</li>
#end
#foreach循環 $allProducts列表 . 每次遍歷, $allProducts 里面的單個值將被賦值給$product.
</ul>
上面這是List或者數組的寫法, 下面是map的寫法
<ul>
#foreach( $key in $allProducts.keySet() )
<li>鍵: $key -> 值: $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( $user in $userList )
$user.name
#if( !$foreach.hasNext )
我是最后一個了
#end
#end
如果你想從零開始的#foreach循環, 你可以使用 $foreach.index 代替$foreach.count. 同樣的, $foreach.first 和 $foreach.last 也提供了$foreach.hasNext方式.如果你想訪問 #foreach外面的這些屬性, 你能夠引用它們通過 $foreach.parent或 $foreach.topmost 屬性 (e.g. $foreach.parent.index 或者 $foreach.topmost.hasNext). 你也可以設置最大的循環執行次數. 默認情況下沒有設置 (可以指定一個值 0 或者更小的), 可以設置一個數字在velocity.properties 的配置文件里面.
directive.foreach.maxloops = -1
你想停止一個foreach 循環在你的模板中, 你可以使用 #break指令在任何時候停止循環:
#foreach( $customer in $customerList )
#if( $foreach.count > 5 )
#break
#end
$customer.Name
#end
引入
#
include腳本元素準許設計者引入一個本地文件, 然后插入到你#include 指令所在的地方。文件的內容不經過模板引擎處理. 由于安全的原因,這個文件僅僅能夠放在 TEMPLATE_ROOT下面.
引用多個文件:
#include( "one.gif","two.txt","three.htm" )
變量也可以作為文件名
#include( "greetings.txt", $seasonalstock )
解析
#
parse腳本元素準許模板設計者引用一個包含VTL的本地文件。Velocity將解析其中的VTL并輸出里面的元素。
#parse( "me.vm" )
注意:任何被 #parse 指令引用的模板必須放在TEMPLATE_ROOT下面.