官網鏈接:http://freemarker.foofun.cn/index.html
3.3.1 基本指令之if指令 <#if condition> 和 ,多個if ,else, else if
3.3.2 基本指令之list指令 item,加逗號分隔,list, items, sep, else
<#list sequence as loopVariable>repeatThis
3.4.8 類型------子程序(函數/方法和用戶自定義指令的比較)
官網鏈接:http://freemarker.foofun.cn/index.html
1.是什麼
FreeMarker 是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。
模板編寫爲FreeMarker Template Language (FTL)。它是簡單的,專用的語言, 不是 像PHP那樣成熟的編程語言。 那就意味着要準備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之後模板顯示已經準備好的數據。在模板中,你可以專注於如何展現數據, 而在模板之外可以專注於要展示什麼數據。
2.爲什麼
這種方式通常被稱爲 MVC (模型 視圖 控制器) 模式,對於動態網頁來說,是一種特別流行的模式。 它幫助從開發人員(Java 程序員)中分離出網頁設計師(HTML設計師)。設計師無需面對模板中的複雜邏輯, 在沒有程序員來修改或重新編譯代碼時,也可以修改頁面的樣式。
而FreeMarker最初的設計,是被用來在MVC模式的Web開發框架中生成HTML頁面的,它沒有被綁定到 Servlet或HTML或任意Web相關的東西上。它也可以用於非Web應用環境中。
FreeMarker 是 免費的, 基於Apache許可證2.0版本發佈。
3.如何用
3.1 模板 + 數據模型 = 輸出
假設在一個在線商店的應用系統中需要一個HTML頁面,和下面這個頁面類似:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome John Doe!</h1>
<p>Our latest product:
<a href="products/greenmouse.html">green mouse</a>!
</body>
</html>
這裏的用戶名(上面的"Big Joe"),應該是登錄這個網頁的訪問者的名字, 並且最新產品的數據應該來自於數據庫,這樣它才能隨時更新。那麼不能直接在HTML頁面中輸入它們, 不能使用靜態的HTML代碼。此時,可以使用要求輸出的 模板。 模板和靜態HTML是相同的,只是它會包含一些 FreeMarker 將它們變成動態內容的指令:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
模板文件存放在Web服務器上,就像通常存放靜態HTML頁面那樣。當有人來訪問這個頁面, FreeMarker將會介入執行,然後動態轉換模板,用最新的數據內容替換模板中 ${...}
的部分, 之後將結果發送到訪問者的Web瀏覽器中。訪問者的Web瀏覽器就會接收到例如第一個HTML示例那樣的內容 (也就是沒有FreeMarker指令的HTML代碼),訪問者也不會察覺到服務器端使用的FreeMarker。 (當然,存儲在Web服務器端的模板文件是不會被修改的;替換也僅僅出現在Web服務器的響應中。)
請注意,模板並沒有包含程序邏輯來查找當前的訪問者是誰,或者去查詢數據庫獲取最新的產品。 顯示的數據是在 FreeMarker 之外準備的,通常是一些 "真正的" 編程語言(比如Java) 所編寫的代碼。模板作者無需知道這些值是如何計算出的。事實上,這些值的計算方式可以完全被修改, 而模板可以保持不變,而且頁面的樣式也可以完全被修改而無需改動模板。 當模板作者(設計師)和程序員不是同一人時,顯示邏輯和業務邏輯相分離的做法是非常有用的, 即便模板作者和程序員是一個人,這麼來做也會幫助管理應用程序的複雜性。 保證模板專注於顯示問題(視覺設計,佈局和格式化)是高效使用模板引擎的關鍵。
爲模板準備的數據整體被稱作爲 數據模型。 模板作者要關心的是,數據模型是樹形結構(就像硬盤上的文件夾和文件),在視覺效果上, 數據模型可以是:
(root)
|
+- user = "Big Joe"
|
+- latestProduct
|
+- url = "products/greenmouse.html"
|
+- name = "green mouse"
Note:
上面只是一個形象化顯示;數據模型不是文本格式,它來自於Java對象。 對於Java程序員來說,root就像一個有 getUser()
和 getLatestProduct()
方法的Java對象, 也可以有 "user"
和 "latestProducts"
鍵值的Java Map
對象。相似地,latestProduct
就像是有 getUrl()
和 getName()
方法的Java對象。
早期版本中,可以從數據模型中選取這些值,使用 user
和 latestProduct.name
表達式即可。如果我們繼續類推, 數據模型就像一個文件系統,那麼 "(root)" 和 latestProduct
就對應着目錄(文件夾),而 user
, url
和 name
就是這些目錄中的文件。
總的來說,模板和數據模型是FreeMarker來生成輸出(比如第一個展示的HTML)所必須的:
模板 + 數據模型 = 輸出
3.2 數據模型一覽(重要)
3.2.1 數據模型1-子變量
正如已經看到的,數據模型的基本結構是樹狀的。 這棵樹可以很複雜,並且可以有很大的深度,比如:
上圖中的變量扮演目錄的角色(比如 root, animals
, mouse
, elephant
, python
, misc
) 被稱爲 hashes (哈希表或哈希,譯者注)。哈希表存儲其他變量(被稱爲 子變量), 它們可以通過名稱來查找(比如 "animals", "mouse" 或 "price")。
存儲單值的變量 (size
, price
, message
和 foo
) 稱爲 scalars (標量,譯者注)。
如果要在模板中使用子變量, 那應該從根root開始指定它的路徑,每級之間用點來分隔開。要訪問 mouse
的 price
,要從root開始,首先進入到 animals
,之後訪問 mouse
,最後訪問 price
。就可以這樣來寫 animals.mouse.price
。
3.2.1 數據模型2----sequences
另外一種很重要的變量是 sequences (序列,譯者注)。 它們像哈希表那樣存儲子變量,但是子變量沒有名字,它們只是列表中的項。 比如,在下面這個數據模型中, animals
和 misc.fruits
就是序列:
要訪問序列的子變量,可以使用方括號形式的數字索引下標。 索引下標從0開始(從0開始也是程序員的傳統),那麼第一項的索引就是0, 第二項的索引就是1等等。要得到第一個動物的名稱的話,可以這麼來寫代碼 animals[0].name
。要得到 misc.fruits
中的第二項(字符串"banana"
)可以這麼來寫 misc.fruits[1]
。(實踐中,通常按順序遍歷序列,而不用關心索引, 這點會在 後續介紹。)
3.2.3 標量類型的分類:
-
字符串:就是文本,也就是任意的字符序列,比如上面提到的 ''m'', ''o'', ''u'', ''s'', ''e''。比如
name
和size
也是字符串。 -
數字:這是數值類型,就像上面的
price
。 在FreeMarker中,字符串"50"
和數字50
是兩種完全不同的東西。前者是兩個字符的序列 (這恰好是人們可以讀的一個數字),而後者則是可以在數學運算中直接被使用的數值。 -
日期/時間: 可以是日期-時間格式(存儲某一天的日期和時間), 或者是日期(只有日期,沒有時間),或者是時間(只有時間,沒有日期)。
-
布爾值:對應着對/錯(是/否,開/關等值)類似的值。 比如動物可以有一個
protected
(受保護的,譯者注) 的子變量, 該變量存儲這個動物是否被保護起來的值。
3.2.4 小結
數據模型可以被看成是樹形結構。
標量用於存儲單一的值。這種類型的值可以是字符串,數字,日期/時間或者是布爾值。
哈希表是一種存儲變量及其相關且有唯一標識名稱的容器。
序列是存儲有序變量的容器。存儲的變量可以通過數字索引來檢索,索引通常從0開始。
3.3 模板一覽(重要,重要)
最簡單的模板通常是普通的HTML文件(或者是其他任何文本文件; FreeMarker本身不屬於HTML)。當客戶端訪問某個頁面時, FreeMarker要發送HTML代碼至客戶端瀏覽器中去顯示。如果想要頁面動起來 (這裏指動態網頁技術,譯者注),那麼就要在HTML中放置能被FreeMarker所解析的特殊代碼片段:
${...}
: FreeMarker將會輸出真實的值來替換大括號內的表達式,這樣的表達式被稱爲 interpolation(插值,譯者注)。
FTL 標籤 (FreeMarker模板的語言標籤): FTL標籤和HTML標籤有一些相似之處,但是它們是FreeMarker的指令,是不會在輸出中打印的。 這些標籤的名字以 #
開頭。(用戶自定義的FTL標籤則需要使用 @
來代替 #
,但這屬於更高級的話題了。)
註釋: 註釋和HTML的註釋也很相似, 但是它們使用 <#--
and -->
來標識。 不像HTML註釋那樣,FTL註釋不會出現在輸出中(不出現在訪問者的頁面中), 因爲 FreeMarker會跳過它們。
其他任何不是FTL標籤,插值或註釋的內容將被視爲靜態文本, 這些東西不會被FreeMarker所解析;會被按照原樣輸出出來。
FTL標籤也被稱爲 指令。 這些指令在HTML的標籤 (比如: <table>
和 </table>
) 和HTML元素 (比如: table
元素) 中的關係是相同的。(如果現在還沒有感覺到它們的不同, 那麼把“FTL標籤”和“指令”看做是同義詞即可。)
可以在 http://freemarker-online.kenshoo.com/ 上很方便的嘗試編寫模板
3.3.1 基本指令之if指令 <#if condition>
和 </#if>
,多個if ,else, else if
使用 if
指令可以有條件地跳過模板的一些片段。
比如,假設在 最初的示例 中, 想向你的老闆Big Joe特別地問好,可其他人不同:
此時,告訴 FreeMarker,當和 "Big Joe"
相同時 ", our beloved leader" (我們最尊敬的領導,譯者注) 纔是if條件中那唯一的 user
變量的值。 通常來講,如果 condition
是false(布爾值),那麼介於<#if condition>
和 </#if>
標籤中的內容會被略過。
condition
的使用:
==
是用來判斷它兩側的值是否相等的操作符, 比較的結果是布爾值,也就是true或者false。
在 ==
的左側,是 被引用的變量, 我們很熟悉這樣的語法結構;最終它會被變量的值所替代。
通常來說, 在指令或插值中沒有被引號標註的內容都被視爲變量的引用。
右側則是指定的字符串, 在模板中的字符串 只能 放在引號內。
當價格爲0時,就會打印出 "Pythons are free today!":
<#if animals.python.price == 0> Pythons are free today! </#if>
和之前示例中,字符串被直接指定相似, 但這裏則是數字(0
)被直接指定了。 請注意,這裏的數字 沒有 放在引號內。 如果將("0"
)放在引號中, 那麼FreeMarker就會將其誤判爲字符串了(也就是字符串0,譯者注)。
當價格不爲0時,則會打印出"Pythons are not free today!":
<#if animals.python.price != 0> Pythons are not free today! </#if>
!=
就是"不等於"。
<#if animals.python.price < animals.elephant.price> Pythons are cheaper than elephants today. </#if>
使用 <#else>
標籤可以指定當條件爲false時程序所要執行的內容。比如:
這個示例中,如果蟒蛇的價格比大象的價格低的話, 程序將會打印出 "Pythons are cheaper than elephants today."。 否則會打印 "Pythons are not cheaper than elephants today."。 後面也可以使用 elseif
來完善它:
如果變量本身就是布爾值(true/false),則可以直接讓其作爲 if
的 condition
(判斷條件,譯者注):
3.3.2 基本指令之list指令 item,加逗號分隔,list
, items
, sep
, else
<#list sequence as loopVariable>repeatThis</#list>
當需要列表顯示內容時,list指令是必須的。
list
指令的一般格式爲: <#list sequence as loopVariable>repeatThis</#list>
。
repeatThis
部分將會在給定的 sequence
遍歷時在每一項中重複, 從第一項開始,一個接着一個。
在所有的重複中, loopVariable
將持有當前遍歷項的值。
這個變量僅存在於 <#list ...>
和 </#list>
標籤內。
sequence
可以是任意表達式, 比如我們可以列表顯示示例數據模型中的水果,就像這樣:
另一個列表相關的常見任務是:使用一些分隔符來列出水果,比如逗號:
所有的這些指令(list
, items
, sep
, else
)可以聯合起來使用:
3.3.3 基本指令之include
使用 include
指令, 我們可以在模板中插入其他文件的內容。
假設要在一些頁面中顯示版權聲明的信息。那麼可以創建一個文件來單獨包含這些版權聲明, 之後在需要它的地方插入即可。比方說,我們可以將版權信息單獨存放在頁面文件 copyright_footer.html
中:
3.3.4 聯合指令
在頁面上也可以多次使用指令,而且指令間也可以很容易地相互嵌套。 比如,在 list
指令中嵌套 if
指令:
請注意,FreeMarker並不解析FTL標籤以外的文本、插值和註釋, 上面示例在HTML屬性中使用FTL標籤也不會有問題。
3.3.5 使用內建函數
內建函數很像子變量(如果瞭解Java術語的話,也可以說像方法), 它們並不是數據模型中的東西,是 FreeMarker 在數值上添加的。 爲了清晰子變量是哪部分,使用 ?
(問號)代替 .
(點)來訪問它們。常用內建函數的示例:
3.3.6 處理不存在變量 加! 或者 ??
數據模型中經常會有可選的變量(也就是說有時並不存在)。 除了一些典型的人爲原因導致失誤外,FreeMarker 絕不能容忍引用不存在的變量, 除非明確地告訴它當變量不存在時如何處理。這裏來介紹兩種典型的處理方法。
這部分對程序員而言: 一個不存在的變量和一個是 null
值的變量, 對於FreeMarker來說是一樣的,所以這裏所指的"丟失"包含這兩種情況。
3.4 數值,類型
3.4.1 基本內容-數值
變量 user
的 value 是"Big Joe"(字符串), today
的 value 是 Jul 6, 2007 (日期),todayHoliday
的 value 是false(布爾值,比如yes/no等值)。 lotteryNumbers
的 value 是包含20,14, 42, 8, 15的序列。當然在這種意義上, lotteryNumbers
是多值類型。它 包含 多個值(比如,其中的第二項的 value 是14),但是 lotteryNumbers
本身還是單值。它就像一個裝有其它很多東西的盒子 (或稱之爲容器,譯者注),但盒子作爲整體還是視作單獨的。最後還有一個數值 cargo
,它的 value 是一個哈希表 (也是類似盒子一樣的東西)。所以說,數值就是存儲在變量中的(比如,在 user
或 cargo
或 cargo.name
中)的那個東西。但是, 不需要存儲於變量之中的數值也可以稱之爲數值,比如下面的數字100:
3.4.2 基本內容-類型
數值中非常重要的一個概念就是類型。比方說,變量 user
的類型是字符串,變量 lotteryNumbers
的類型是序列。數值的類型這個概念非常的重要,因爲它決定了這些數值可以在哪裏使用的最大限度。 比如說,使用 ${user / 2}
就是錯誤的,但是使用 ${cargo.weight / 2}
就能計算出結果,爲20, 因爲算術中的除法僅對數字類型的值有效,而不能用於字符串。 僅當 cargo
是一個哈希表變量時,表達式 cargo.name
纔可以使用點。也可以用 <#list ...>
指令來遍歷序列。而 <#if ...>
指令的條件只能是布爾值等。
3.4.3 基本內容-數據模型是哈希表
注意觀察每個數據模型的例子你也許能發現:被"(root)"所標識的內容就是哈希表類型的值。 當編寫如 user
這樣的代碼時,那就意味着要把"user"變量存儲在哈希表的根上。 就像編寫 root.user
一樣,這裏但並沒有名"root"爲的變量, 那麼這就起不到任何作用了。
某些人也許會被這種數據模型的例子所困惑,也就是說,根哈希表包含更多的哈希表或序列 (lotteryNumbers
and cargo
)。其它就沒有更特殊的內容了。 哈希表包含其他變量,那些變量包含其它值,這些數值可以是字符串,數字等變量, 當然也可以是哈希表或序列變量。最初我們解釋過的,就像字符串和數字, 序列或哈希表也是一種值的表示形式。
3.4.4 類型------標量
3.4.5 類型------容器
3.4.6 類型------子程序(方法和函數)
3.4.7 類型------子程序(用戶自定義指令)
3.4.8 類型------子程序(函數/方法和用戶自定義指令的比較)
3.4.9 類型------結點