自定義指令可以使用macro指令來定義。java程序員若不想在模板中實現定義指令,而是在Java語言中實現指令的定義,這時可以使用freemarker.template.TemplateDirectiveModel類來擴展,后邊會講。 4.1.2 基本內容
宏是有一個變量名的模板片段。你可以在模板中使用宏作為自定義指令,這樣就能進行重復性的工作。例如,創建一個宏變量來打印大號的”hello Joe!“;
<#macro greet> <font size="+2">Hello Joe!</font></#macro>可以在FTL標記中通過@代替#來使用自定義指令。
<@greet></@greet>4.1.3 參數我們僅在greet宏中定義一個變量,person;
<#macro greet person> <font size="+2">Hello ${person}!</font></#macro>那么可以這樣使用宏:
<@greet person="Fred"/>and<@greet person="Batman"/>可以去掉雙引號, <@greet person=Fred/>
多個參數時,可以設置默認值,要不然的話使用的時候必須指定對應的多個參數:
<#macro greet person color="black"> <font size="+2" color="${color}">Hello ${person}!</font></#macro>那么就可以這樣使用了
<@greet person="Fred"/>實例/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/macro.ftl
<h2>沒有參數</h2> <p> <#macro greet> <font size="+2">Hello Joe!</font> </#macro> <@greet/> </p> <h2>有參數</h2> <p> <#macro greet person> <font size="+2">Hello ${person}!</font> </#macro> <@greet person="Fred"/> and <@greet person="Bob"/> </p> <h2>多個參數時,可以設定默認值</h2> <p> <#macro greet person color="black"> <font size="+2" color="${color}">Hello ${person}!</font> </#macro> <@greet person="Fred"/> and <@greet person="Bob" color="red"/> </p>輸出:
4.1.4 嵌套內容
自定義指令可以嵌套內容,和預定義指令相似:<#if ...>nested content</#if>。比如,下面這個例子中是創建了一個可以為嵌套的內容畫出邊框;
<#macro border> <table border=4 cellspacing=0 cellpadding=4> <tr> <td> <#nested> </td> </tr> </table></#macro><#nested>標簽執行位于開始和結束標簽之間的模板代碼塊,例如:
<@border>The bordered text</@border>輸出:
<table border=4 cellspacing=0 cellpadding=4> <tr> <td> The bordered text </td> </tr></table><#nested>指令可以被執行多次;
<#macro do_thrice> <#nested> <#nested> <#nested></#macro>使用時:
<@do_thrice>anything</@do_trice>輸出:
anythinganythinganything如果不使用nested指令,那么嵌套的內容就會被執行,如果不小心寫成這樣:
<@greet person="Joe"> Anything.</@greet>輸出是:
<font size="+2">Hello Joe!</font>嵌套的內容被忽略了,因為greet沒有使用nested指令;
嵌套內容可以是其他FTL指令,包含其他自定義指令也是可以的。
<@border> <ul> <@do_thrice> <li><@greet person="Joe"></li> </@do_thrice> </ul></@border>在嵌套的內容中,宏的局部變量是不可見的;
<#macro repeat count> <#local y="test"> <#list 1..count as x> ${y} ${count}/${x} : <#nested> </#list></#macro><@repeat count=3>${y!"?"} ${x!"?"} ${count!"?"}</@repeat>將會輸出:
test 3/1: ? ? ?test 3/2: ? ? ?test 3/3: ? ? ?不同的局部變量的設置是為每個宏自己調用的,所以不會導致混亂;
<#macro test foo>${foo} (<#nested>) ${foo}</#macro><@test foo="A"><@test foo="B"><@test foo="C"/></@test></@test>輸出:
A (B (C () C) B) A4.1.5 宏與循環變量循環變量的名字是已經給定了,變量值的設置由指令本身完成。
<#macro do_thrice> <#nested 1> <#nested 2> <#nested 3></#macro><p> <#--自定義循環變量需要用;代替as--> <@do_thrice ; x> do_something : ${x} </@do_thrice></p>輸出:
do_something : 1 do_something : 2 do_something : 3 例子:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/nested.ftl
<h3>宏與循環變量</h3><#macro do_thrice> <#nested 1> <#nested 2> <#nested 3></#macro><p> <#--自定義循環變量需要用;代替as--> <@do_thrice ; x> do_something : ${x} </@do_thrice></p>一個宏可以使用多個循環變量(變量的順序是很重要的):
<#macro repeat count> <#list 1..count as x> <#nested x, x/2, x==count> </#list></#macro>使用的時候:
<@repeat count=4 ; c, halfc, last> ${c}.${halfc}<#if last> Last!</#if></@repeat>那么將會輸出:
/1. 0.5/2. 1/3. 1.5/4. 2 Last!例子:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/nested.ftl
<h3>一個宏可以使用多個循環變量</h3> <#macro repeat count> <#list 1..count as x> <#nested x, x/2, x==count><br> </#list> </#macro> <p> <@repeat count=4 ; c, half, last> ${c}. ${half} <#if last> Last!</#if> </@repeat> </p>如果分號后指定的變量少了或多了,多出的會忽略掉;
@repeat count=4 ; c, half> ${c}. ${half} </@repeat>4.1.6 自定義指令和宏進階你也可以在FTL中定義方法,參見function指令;
也許你對命名空間感興趣。命名空間可以幫助你組織和重用你經常使用的宏;4.2 在模板中定義變量
可以訪問一個在模板里定義的變量,就像是訪問數據模型根上的變量一樣。這個變量比定義在數據模型中的同名參數具有更高的優先級。如果你恰巧定義了一個名為“foo”的變量,而在數據模型中也有一個名為“foo”的變量,那么模板中的變量就會將數據模型根上的變量隱藏(而不是覆蓋?。?p>在模板中可以定義三種類型的變量:
include指令引入的模板訪問??梢允褂?code>assign或macro指令來創建或替換這些變量。 list)自動創建的,而且它們只在指令的開始和結束標記內有效。宏的參數是局部變量而不是循環變量。使用assign創建和替換變量;
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl
<h3>簡單變量</h3> <p> <#assign x=1> <#--創建變量x--> ${x}<br> <#assign x=x+3> <#--替換變量x--> ${x} </p>輸出:
簡單變量14 局部變量也會隱藏(而不是)同名的簡單變量。循環變量也會隱藏(不是覆蓋)同名的局部變量和簡單變量。
實例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl
<#macro test> 2. ${x}<br> <#local x="local"> 3. ${x}<br> <#list ["loop"] as x> 4. ${x}<br> </#list> 5. ${x}<br> </#macro> <p> <#assign x="plain"> 1. ${x}<br> <@test/> 6. ${x}<br> <#list ["loop"] as x> 7. ${x}<br> <#assign x="plain2"> <#--在這里替換了簡單變量x--> 8. ${x}<br> </#list> 9. ${x} </p>輸出:
1. plain2. plain3. local4. loop5. local6. plain7. loop8. loop9. plain2內部循環可以隱藏外部循環的變量;
實例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl
<#list ["loop 1"] as x> ${x}<br> <#list ["loop 2"] as x> ${x}<br> <#list ["loop 3"] as x> ${x}<br> </#list> ${x}<br> </#list> ${x}<br> </#list>輸出:
loop 1loop 2loop 3loop 2loop 1有時發生一個變量隱藏數據模型中的同名變量,但是如果想訪問數據模型中的變量,就可以使用特殊變量globals。
實例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl
<#assign user="Cindy"> ${user}, ${.globals.user} /FreeMarker-hello-web/src/main/java/org/yejq/fre/service/Exercises.java
public void testVariable(Model model){ model.addAttribute("user", "lucy"); }測試:http://localhost/test/4/variable/testVariable,輸出結果:
Cindy, lucy4.3 命名空間4.3.1 簡介如果想創建可以重復使用的宏、函數和其他變量的集合,通常用術語來說就是引用library庫,使用命名空間是必然的;4.3.2 創建一個庫
實例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl
<#macro copyright date> <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p></#macro><#assign mail="jsmith@acme.com">/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl
<h3>使用import導入</h3> <p> <#import "../lib/my_test.ftl" as my> <@my.copyright date="2014-2016"/> ${my.mail} </p>輸出:
使用import導入Copyright (C) 2014-2016 Julia Smith. All rights reserved.jsmith@acme.com 測試不同的命名空間;
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl
<#macro copyright date> <p>Copyright (C) ${date} Julia Smith. All rights reserved. <br>Mail: ${mail} </p></#macro><#assign mail="jsmith@acme.com">/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl
<h3>演示不同的命名空間</h3> <p> <#import "../lib/my_test.ftl" as my> <#assign mail="fred@acme.com"> <@my.copyright date="2014-2016"/> ${my.mail}<br> ${mail} </p>測試:http://localhost/test/4/namespace/null4.3.3 在引入的命名空間上編寫變量
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl
<#import "../lib/my_test.ftl" as my> ${my.mail}<br> <#assign mail="jsmith@other.com" in my> ${my.mail}http://localhost/test/4/namespace/null,輸出:
jsmith@acme.comjsmith@other.com 4.3.4 命名空間和數據模型數據模型中的變量在任何位置都是可見的。如果在數據模型中有一個名為user的變量,那么lib/my_test.ftl也能訪問TA。
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl
<#macro copyright date> <p>Copyright (C) ${date} ${user} Julia Smith. All rights reserved. <br>Mail: ${mail} </p></#macro><#assign mail="${user}@acme.com">/FreeMarker-hello-web/src/main/java/org/yejq/fre/service/Exercises.java
public void testNamespace(Model model){ model.addAttribute("user", "lucy"); }/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl
<h3>數據模型中的變量在任何位置都是可見的</h3> <p> <#import "../lib/my_test.ftl" as my> <@my.copyright date="2014-2015"/> </p>測試:http://localhost/test/4/namespace/testNamespace?11,輸出結果:
Copyright (C) 2014-2015 lucy Julia Smith. All rights reserved. Mail: lucy@acme.com 4.3.5 命名空間的生命周期命名空間由使用的import指令中所寫的路徑來識別。如果想多次import這個路徑,那么只會為第一次的import引用創建命名空間執行模板。后邊相同的路徑的import只是創建一個哈希表當做訪問相同命名空間的“門”。
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl
<h3>命名空間的聲明周期</h3> <p> <#import "/lib/my_test.ftl" as my> <#import "/lib/my_test.ftl" as foo> <#import "/lib/my_test.ftl" as bar> <#--只會在第一次創建命名空間執行模板,之后相同路徑的import只是創建哈希表當做訪問相同命名空間的"門"--> ${my.mail}, ${foo.mail}, ${bar.mail}<br> <#assign mail="jsmith@other.com" in my> ${my.mail}, ${foo.mail}, ${bar.mail}<br> <#--其中一個命名空間的值修改了,所有都相應修改,就像java里邊的對象引用修改一樣--> <#assign mail="jsmith@foo.com" in foo> ${my.mail}, ${foo.mail}, ${bar.mail} </p>訪問:http://localhost/test/4/namespace/null,輸出:
命名空間的聲明周期jsmith@acme.com, jsmith@acme.com, jsmith@acme.comjsmith@other.com, jsmith@other.com, jsmith@other.comjsmith@foo.com, jsmith@foo.com, jsmith@foo.com 還要注意命名空間是不分層次的,它們相互之間是獨立存在的。那么,如果在命名空間N1中import命名空N2,那N2也不在N1中,N1只是通過哈希表訪問N2。這和主命名空間中importN2,然后直接訪問命名空間N2是一樣的過程。
每一次模板的執行過程,它都有一個私有的命名空間的集合。每一次模板執行工作都是一個分離且有序的過程,它們僅僅存在一段很短的時間,同時頁面用以呈現內容,然后就和所有填充過的命名空間一起消失了。4.3.6 為他人編寫庫
http://freemarker.org/libraries.html
標準庫路徑的格式:
/lib/yourcompany.com/your_library.ftl如果你的公司的主頁是www.example.com,那么;
/lib/example.com/widget.ftl/lib/example.com/commons/string.ftl一個重要的規則是路徑不應該包含大寫字母,winForm改成win_form;4.4 空白處理4.4.1 簡介
來看看這個模板;

按照Freemarker規則輸出后是:

這么多多余的空白是很令人頭疼,而且增加處理后的HTML文件大小也是沒必要的;
FreeMarker提供了以下工具來處理這個問題:
t, rt和lt,使用這些指令可以明確告訴FreeMarker去忽略某些空白; strip_text:這將從模板中刪除所有頂級文本。對模板來說很有用,它包含某些定義的宏,因為它可以移除宏定義和其他頂級指令中的換行符,這樣可以提高模板的可讀性;comPRess指令它會自動忽略兩種典型的空白:
<#if ...>x,那么空白不會忽略,因為x不是標簽。而一行上有<#if ...> <#list ...>,這樣也不會忽略空白,因為標簽之間的空格是嵌入空白; 使用剝離空白之后,上面的例子輸出是:

剝離空白功能可以在ftl指令在模板中開啟或關閉。默認開啟。開啟剝離空白不會降低模板執行的效率,剝離空白的操作在模板加載時就已經完成了。
剝離空白可以為單獨的一行關閉,就是使用nt指令;4.4.3 使用compress指令
和剝離空白相反,這個工作是直接基于生產的輸出內容,而不是對模板進行的。它會強勢地移除縮進,空行和重復的空格/制表符;
對于下邊這段代碼:
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/compress.ftl
<h2>使用compress指令,在輸出的內容中移除縮進,空行和空格/制表符</h2> <p> <#compress> <#assign users = [{"name":"Joe", "hidden":false}, {"name":"James Bond", "hidden":true}, {"name":"Julia", "hidden":false}]> list of users:<br> <#list users as user> <#if !user.hidden> - ${user.name}<br> </#if> </#list> </#compress> </p>輸出:
<h2>使用compress指令,在輸出的內容中移除縮進,空行和空格/制表符</h2> <p> list of users:<br>- Joe<br>- Julia<br> </p>由于向下兼容性,名稱為compress的用戶自定義指令是存在的,有個single_line的屬性,如果設置為true,那么會移除其中的換行符;
<h3>@compress的屬性single_line</h3> <p> <@compress single_line=true> <#assign users = [{"name":"Joe", "hidden":false}, {"name":"James Bond", "hidden":true}, {"name":"Julia", "hidden":false}]> list of users:<br> <#list users as user> <#if !user.hidden> - ${user.name}<br> </#if> </#list> </@compress> </p>貌似不起作用,反而#compress是single_line;4.5 替換(方括號)語法
版本2.3.4之后才有;
在指令和注釋中使用[]代替<>;
[#list ...]...[/list][@mymacro .../][#-- the comment --]為了使用這種語法從而代替默認語法,那么就需要用[#ftl]來開始模板。
/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/ftl.ftl
[#ftl]<!doctype html><html lang="en"><head> <meta charset="UTF-8" /> <title>Document</title></head><body> <h3>替換語法</h3> <p> [#assign users = [{"name":"Joe", "hidden":false}, {"name":"James Bond", "hidden":true}, {"name":"Julia", "hidden":false}]] list of users:<br> [#list users as user] [#if !user.hidden] - ${user.name}<br> [/#if] [/#list] </p></body></html>測試:http://localhost/test/4/ftl/null
項目
新聞熱點
疑難解答