Groovy模板引擎下(The MarkupTemplateEngine引擎細節介紹)
模板格式
1.1基礎:
模板包含Groovy代碼,下麵詳細具體的解析一下第一個樣例:
xmlDeclaration() (1)
cars { (2)
cars.each { (3)
car(make: it.make, model: it.model) (4)
} (5)
}
1 渲染XML的聲明字符串
2 打開一個cars標簽
3 cars是模板數據模型的一個變量包含了所有的car實例
4 遍曆每一項,從car實例創建car標簽
5 關閉上述的cars標簽
模板可以使用通常的Groovy代碼,能在list(來自模板數據模型)上使用each為每個car實例生成一個car標簽。
相似的情景下,渲染HTML代碼非常簡單:
yieldUnescaped '<!DOCTYPE html>' (1)
html(lang:'en') { (2)
head { (3)
meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"') (4)
title('My page') (5)
} (6)
body { (7)
p('This is an example of HTML contents') (8)
} (9)
}
1 渲染HTML文件類型的標簽
2 打開一個帶有屬性的html標簽
3 打開一個head標簽
4 渲染帶有http-equiv屬性的 meta標簽
5 渲染title標簽
6 關閉head 標簽
7 打開head標簽
8 渲染P標簽
9 關閉body標簽
10 關閉html標簽
輸出結果如下:
<!DOCTYPE html><html lang='en'><head><meta http-equiv='"Content-Type" content="text/html; charset=utf-8"'/><title>My page</title></head><body><p>This is an example of HTML contents</p></body></html>
通過一些配置,能美化輸出,增加一些換行以及縮進。
1.2支持的方法
上述示例,文檔類型通過yieldUnescaped方法渲染。同時我們也見到過xmlDeclaration方法。模板引擎支持多種方法幫助渲染內容。
方法名 | 描述 | 樣例 |
yield | 渲染內容,但是渲染前會轉義 | 模板:yield ‘Some text with <angle brackets>’輸出:Some text with <angle brackets> |
yieldUnescaped | 渲染原始內容,渲染後不進行轉義 | 模板:yieldUnescaped ‘Some text with <angle brackets>’輸出:Some text with <angle brackets> |
xmlDeclaration | 渲染XML聲明字符串。如果在配置文件中編碼進行了聲明,則會寫入XML聲明 | 模板:xmlDeclaration()輸出:<?xml version=’1.0’?>如果TemplateConfiguration#getDeclarationEncoding非空輸出:<?xml version=’1.0′ encoding=’UTF-8’?> |
comment | 渲染原始內容到XML的注釋中 | 模板:comment ‘This is <a href=’https://docs.groovy-lang.org/latest/html/documentation/foo.html’>commented out</a>’輸出:<!–This is <a href=’https://docs.groovy-lang.org/latest/html/documentation/foo.html’>commented out</a>–> |
newLine | 渲染新行同時參考:TemplateConfiguration#setAutoNewLine和TemplateConfiguration#setNewLineString. | 模板:p(‘text’)newLine()p(‘text on new line’)輸出:<p>text</p><p>text on new line</p> |
pi | 渲染XML處理聲明 | 模板:pi(“xml-stylesheet”:[href:”mystyle.css”, type:”text/css”])輸出:<?xml-stylesheet href=’mystyle.css’ type=’text/css’?> |
tryEscape | 如果是對象字符串,從實體對象渲染轉義的字符串,否則返回實體對象本身 | 模板:yieldUnescaped tryEscape(‘Some text with <angle brackets>’)輸出:Some text with <angle brackets> |
1.3 包含
MarkupTemplateEngine模板支持從其他文件引入內容,引入的類型可以是:
- 另一個模板
- 原始內容
- 轉義的內容
引入另外的模板如下:
include template: 'other_template.tpl'
引入原始內容文件(無需轉義)如下:
include unescaped: 'raw.txt'
最終,引入需要渲染前轉義的txt文件如下:
include escaped: 'to_be_escaped.txt'
選擇性的,可以使用如下方法代替:
includeGroovy(<name>) 引入其他模板
includeEscaped(<name>) 引入其他需要轉義的文件
includeUnescaped(<name>) 引入其他不需要轉義的文件
如果引入的文件名是動態的(存儲在變量中)則使用上述的方法替代 include xxx:語法是非常有用的。引入的文件能在classpath上找到。這是MarkupTemplateEngine接受ClassLoader 作為構造器參數的原因(其他的原因:在模板中引入涉及到其他類的代碼)
如果你不想設置模板到classpath上,MarkupTemplateEngine可以接受一個方便的構造器方便你定義模板所在的文件目錄。
1.4 片段
片段是內嵌的模板,他們在單一的模板中內提高複用性。一個碎片包含:字符串,內嵌模板和模型 用於渲染模板。參考下麵的模板:
1 |
ul { |
2 |
pages.each {
|
3 |
fragment "li(line)" , line:it
|
4 |
}
|
5 |
} |
fragment元素創建內嵌模板,根據指定的模型渲染模板。這裏,我們有li(line)片段, line綁定到it, it對應於pages的遍曆,我們將會為每個page生成單個的li元素.
<ul><li>Page 1</li><li>Page 2</li></ul>
片段對於模板元素的分解非常有意思,對於模板來說,他們需要付出片段編譯的代碼,並且不能外部化。
1.5 布局
布局涉及到其他的模板。通常用於組合模板並且共享通用的結構。如果你的HTML頁麵有通用的布局,並且隻想替換body,那麼布局將是非常有意思的,將簡化操作。首先,需要創建一個布局模板:
layout-main.tpl
html { head { title(title) (1) } body { bodyContents() (2) } }
1. title變量是布局變量
2. 調用bodyContents渲染body
然後需要做的就是在模板中引入布局:
layout 'layout-main.tpl', (1) title: 'Layout example', (2) bodyContents: contents { p('This is the body') } (3)
1.使用 main-layout.tpl布局文件 2.設置 title變量 3.設置 bodyContents 感謝布局文件中對bodyContents()的調用,bodyContents將會被渲染到布局中。結果,模板會被渲染成如下:
Layout example
This is the body
對於contents方法的調用通知模板引擎這塊代碼對於模板是特殊的聲明而不是用於直接渲染的幫助方法。如果沒有在聲明前新增contents方法,那麼內容將直接被渲染,但是你會看到一個隨機生成的字符串而不是代碼塊對應的結果值。
布局是一個有力的方式共享橫跨多個模板的通用內容。不需要重寫以及引入任何東西。
布局所用的數據模型和頁麵所用的數據模型是獨立的。然後繼承頁麵的數據模型也是可行的,設想一下定義的模型如下:
1 |
model = new HashMap<String,Object>(); |
2 |
model.put('title','Title from main model'); |
模板如下:
1 |
layout 'layout-main.tpl', true, (1) |
2 |
bodyContents: contents { p('This is the body') }
|
1. 注意設置成ture開啟模板繼承。
因此,傳遞title的值到先前的例子就變的不是那麼必要。渲染的結果如下:
1 |
<html><head><title>Title from main model</title></head><body><p>This is the body</p></body></html> |
但是重寫來自父模型的值也是可能的:
1 |
layout 'layout-main.tpl', true, (1) |
2 |
title: 'Overriden title', (2)
|
3 |
bodyContents: contents { p('This is the body') }
|
1 true表示從父模型繼承
2 但是title被重寫
輸出的結果如下:
1 |
<html><head><title>Overriden title</title></head><body><p>This is the body</p></body></html> |
2 渲染內容
2.1創建模板引擎
服務端渲染模板引擎需要groovy.text.markup.MarkupTemplateEngine和groovy.text.markup.TemplateConfiguration的實例
1 |
TemplateConfiguration config = new TemplateConfiguration(); (1) |
2 |
MarkupTemplateEngine engine = new MarkupTemplateEngine(config); (2) |
3 |
Template template = engine.createTemplate("p('test template')"); (3) |
4 |
Map<String, Object> model = new HashMap<>(); (4) |
5 |
Writable output = template.make(model); (5) |
6 |
output.writeTo(writer); (6) |
1 創建模板配置
2 基於模板配置創建模板引擎
3 基於字符串創建模板
4 創建用於模板的數據模型
5 綁定數據模型到模板
6 渲染輸出
下麵是解析模板的一些可選擇的項:
- 基於String,使用createTemplate(String)
- 基於Reader, 使用createTemplate(Reader)
- 基於URL, 使用 createTemplate(URL)
- 基於模板名, 使用 createTemplateByPath(String)
通常被建議的最終的版本:
1 |
Template template = engine.createTemplateByPath("main.tpl"); |
2 |
Writable output = template.make(model); |
3 |
output.writeTo(writer); |
2.2 配置選項
通過TemplateConfiguration的一些配置選項,可以配置引擎,改變引擎的默認行為:
選項 | 默認值 | 描述 | 樣例 |
declarationEncoding | null | 定義調用xmlDeclaration時默認的編碼格式,本身不影響輸出 | xmlDeclaration()輸出:
<?xml version=’1.0’?> 如果TemplateConfiguration#getDeclarationEncoding非空:
輸出:
<?xml version=’1.0′ encoding=’UTF-8’?> |
expandEmptyElements | false | 如果true,輸出標簽的擴展格式 | 模板:p()輸出:
<p/> 如果expandEmptyElements 是true: 輸出: <p></p> |
expandEmptyElements | false | 如果true,屬性的雙引號代替單引號 | 模板:tag(attr:’value’)輸出:
<tag attr=’value’/> 如果useDoubleQuotes是true:
輸出: <tag attr=”value”/> |
newLineString | 係統默認(係統屬性line.separator) | 允許選擇新行渲染所使用的字符串 | 模板p(‘foo’)newLine()
p(‘baz’) 如果newLineString=’BAR':
輸出:
<p>foo</p>BAR<p>baz</p> |
autoEscape | false | 如果true,數據模型中的變量會在渲染前自動轉義 | 參見:the auto escape section |
autoIndent | false | 如果true,執行新行後的自動縮進 | 參見the auto formatting section |
autoIndentString | 4空格 | 縮進使用的字符串 | 參見the auto formatting section |
autoNewLine | false | 如果true,執行新行的自動插入 | 參見the auto formatting section |
baseTemplateClass | groovy.text.markup.BaseTemplate | 設置編譯模板的父類,提供應用所需的特殊模板 | 參見the custom templates section |
locale | Default locale | 設置模板默認的語言環境 | 參見the internationalization section |
2.3. 自動格式化
默認的,模板引擎會輸出沒有格式化的文本。參見配置選項,對輸出做必要的格式化
-
autoIndent
新行插入後自動縮進 -
autoNewLine
基於原始默認的模板內容自動插入新行
通常,推薦設置autoIndent和autoNewLine為true,增加可讀性,代碼如下:
1 |
config.setAutoNewLine(true); |
2 |
config.setAutoIndent(true); |
使用下麵的模板:
1 |
html { |
2 |
head {
|
3 |
title('Title')
|
4 |
}
|
5 |
} |
輸出結果:
1 |
<html> |
2 |
<head>
|
3 |
<title>Title</title>
|
4 |
</head>
|
5 |
</html> |
我們可以稍微的調整模板讓title和heade位於同一行
1 |
html { |
2 |
head { title('Title')
|
3 |
}
|
4 |
} |
輸出如下:
1 |
<html> |
2 |
<head><title>Title</title>
|
3 |
</head>
|
4 |
</html> |
新行僅僅插入在花括號標簽之後,同時插入對應於新的內嵌內容被發現的地方。這就意味著標簽中包含的標簽不會觸發新行的插入除非使用花括號。
1 |
html { |
2 |
head {
|
3 |
meta(attr:'value') (1)
|
4 |
title('Title') (2)
|
5 |
newLine() (3)
|
6 |
meta(attr:'value2') (4)
|
7 |
}
|
8 |
} |
1 meta和head不在同一行,新行被出入
2 新行不會被插入,和前麵的標簽處於同一深度
3 調用newLine強製插入新行
4 在新行被渲染
輸出結果如下:
1 |
<html> |
2 |
<head>
|
3 |
<meta attr='value'/><title>Title</title>
|
4 |
<meta attr='value2'/>
|
5 |
</head>
|
6 |
</html> |
默認的情況下,現在使用4個空格縮進,但是可以通過設置TemplateConfiguration#autoIndentString屬性來定製化。
最後更新:2017-05-22 20:04:31