Part I. 设计人员指南 Designer's Guide
Chapter 1. 快速入门 Quick Start
模版 + 数据模型 = 输出 Template + data model = output
FreeMarker是基于这样一个理念的:设计人员与开发人员是不同的人,他们分别擅长不同的技能。理想情况下,应该将设计人员的关注点集中到外观展现上 – 他们创建HTML文件、图片、和web页面的其他方面。同时,开发人员来创建产生数据的系统,再由设计人员设计的页面来显示。
问题是你经常不得不在页面上(或其他文档中)显示一些在设计期未知的信息。一个典型的电子商务应用就是基于这种动态数据的 – 比如库存的实时状态,美元与日元的转换汇率,顾客的订单是否已经发送等。这些数据总是在变。遇到这种情况,你需要再你的HTML(或其他文本中放一些特殊的结构;FreeMarker的输出不仅仅限于HTML),然后FreeMarker会在需要输出页面给用户时将这些代码替换成正确的数据。现在以一个非常简单的例子开始 -- 你需要在欢迎页面显示用户的名称并提示你公司的最近产品:
|
上面的内容就是一个普通的HTML片段(除了其中的特殊代码${...})。这些代码是FreeMarker要发送输出到用户时,用来说明应该将实时的文本信息放到哪里去的。像这样的文件被叫做模版templates。
那么是user, latestProduct.url和latestProduct.nam从哪儿来的呢?他们是从数据模型中复制来的。数据模型是存储在计算机内存中的内容。由开发人员写的程序创建数据模型(即数据模型由程序动态生成),这就是模版提供变化信息的机制。这些信息可能来自数据库、文件甚至是由程序生成的内容都可以。但模版作者其实并不关系数据来源。他们只是使用已有的数据模型。
我们说,输出是通过将模版和数据模型相结合创建的:
Template + data model = output
数据模型可能是这样:
|
(不要误会:数据模型并不是文本,这里只是提供一个数据模型的形象的样子)
打个比方,这就像是文件系统:root和latestProduct对应于目录,user, url和name对应于文件。url和name在目录latestProduct中。但这只是一个比喻,其实并没有真正的文件和目录。
当FreeMarker将上面的数据模型与示例模版合成时,web页面的访问者就会得到如下输出:
|
|
数据模型 The data model
正如你已经看到的,数据模型是基于树形结构的。这棵树可以任意复杂,并且不限深度:
|
其中作为目录的变量(the root, anim al s, mouse, elephant, python, whatnot)叫做散列hashes。哈西使用查找名来存放其它子变量。
存储单个值的变量叫做标量sc al ars。
当你访问一个子变量时,必须使用从根root开始的、以句点.分割的路径。比如要访问老鼠mouse的价格price,从根root开始到anim al s,然后到mouse,再到price。所以应该这样写:anim al s.mouse.price。当你用特殊代码${...}包围这样的表达式,就是告诉FreeMarker输出这个节点的相应文本。比如:${ anim al s.mouse.price }???
还有一个重要的变量:序列sequences。序列与散列类似,都可以包含子变量,但是没有相应的查找名,而是使用序列存储子变量,可以通过数字下标访问子变量。比如,在下面的数据模型中,anim al s和whatnot.fruits都是序列:
|
要访问序列的子变量,需要使用类似于数组的下标机制,将下标放到中括号中。下标从0开始,第二个变量的下标是1,以此类推。所以要获得第一个动物的名字,使用anim al s[0].name。要获得whatnot.fruits中的第二个条目(这里是字符串"banana"),使用whatnot.fruits[1]。
标量可以存储不同类型的值。最常用的类型有:
- 字符串:文本、字符序列。要在FreeMarker中使用字符串,必须使用引号,如"mouse" 或 'mouse'。
- 数字:就是数值。字符串"50"与数值50是完全不同的。前者是两个字符(尽管读音与数字相同),而后者是一个可以用于数学计算的数字值。所以,不要用引号包围数字!这会使FreeMarker将该量作为字符串处理。合法的数字有:0, 4999, -273, 3.14。
总结:
- 数据模型可以视作是层次结构的 -- 就像是树。
- 标量sc al ar存储单值。可以存储字符串或数字(还有别的东西,稍后介绍)。
- 散列hash是用于存储其它变量的容器,并具有查找名。
- 序列sequence是一种按顺序存储其它变量的容器。可以通过数字下标进行访问。
模版 The template
最简单/常见的模版是HTML文件。当客户端访问页面时,FreeMarker就会将该HTML原封不动的发送到客户端。但如果你想获得动态效果,就会在HTML中特定位置放置一些FreeMarker可以识别的东西:
· 占位符${...}:FreeMarker会在输出中使用括号中内容相对应的实际值来替换它。
· FTL标签(FreeMarker Template Language tags):FTL标签与HTML标签相似,但它们是FreeMarker的内容,并不会打印到输出中。FTL标签名以井号”#”开头,以便于HTML标签区分开来。(其实还可以用” @”代替”#”,后续章节会解释)
· 注释Comments:与HTML注释类似,只是由<#-- 和 -->来界定(而不是<!—和 -->)。这两个界定符之间的任何内容都会被FreeMarker忽略,并且不会打印到输出中。
任何既不是FTL标签也不是占位符或注释的内容都被当作普通文本来处理,并且不会被FreeMarker解析,而直接打印到输出中。
FTL标签也称为“指令”directives。就像是HTML标签也被叫做HTML元素,比如<table>和</table>也叫做table元素。如果你感觉二者没有区别,可以把“FTL标签”和“指令”作为同义词。
指令的例子 Examples of directives
尽管FreeMarker有很多的指令,但这个小例子只使用最常用的指令中的三个。
if指令 The if directive
使用if指令可以根据条件跳过模版的一个部分。比如,假设在第一个例子very first example中,你想将你的老板于其它用户区分开来,假设你老板的名字叫Big Joe:
|
在模版中,你告诉FreeMarker字符串“, our beloved leader”只有在变量user的是值是字符串"Big Joe"时才显示。通常,如果条件condition的值为f al se时,在<#if condition>与</#if>标签中的内容。
注意:这里使用了两种不同语法的元素。等号=的左边,使用了一个变量,右边使用了字符串。也可以这样写(使用基于散列样例的数据模型):
|
使用<#else>标签可以制定当条件为假时要做什么:
|
如果python的价格比大象elephant的价格低,会输出”Pythons are cheaper than elephants today.”,否则输出“Pythons are not cheaper than elephants today.”。
list指令 The list directive
当你想列举一些内容的时候,应该使用list指令。如果将这个模版与前面的序列样例数据模型合并:
|
会输出:
|
list指令的通用格式是:
<#list SequenceVar as variable>repeatThis</#list>
<#list 序列变量 as 变量>重复内容</#list>
repeatThis重复内容 部分就是当在给定的序列变量SequenceVar中的每个条目被遍历(从第一个开始)时所要重复的内容。每重复一次,变量variable就会持有当前条目的值。这个变量只存在于<#list ...>与</#list>内,并且不会重写/覆盖任何位于数据模型中的同名变量。
再来一个例子,将前面数据模型中的水果列出来:
|
注意:你应该已经熟悉whatnot.fruits了,它是对数据模型中的变量的引用。
include指令 The include directive
使用include指令可以将外部文件导入模版(译注:与html、jsp中的include类似)。
比如,如果你要在多个页面显示相同的版权信息。可以创建一个只包含版权声明的文件,然后将其插入你需要显示版权声明的任何地方。假设将版权声明放到copyright_footer.html中:
|
在任何需要的地方使用include指令插入即可:
|
会输出:
|
如果修改copyright_footer.html,就会在所有的页面中看到新的版权声明。
结合使用多个指令 Using directives together
你可以在一个页面中使用任意数量的指令,也可以自由的嵌套指令。比如下面的例子会列出所有的动物并将大型动物的名称以粗体显示:
|
注意:由于FreeMarker不对FTL标签外部的文本进行解析,所以占位符和注释不会将上面的<b>和</b>视为嵌套错误的标签。