模板格式
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=’http://docs.groovy-lang.org/latest/html/documentation/foo.html’>commented out</a>’输出:<!–This is <a href=’http://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属性来定制化。