templates - niubods/playframework-notes GitHub Wiki
Play可有一套高效的模板系统来产生动态的HTML、XML、JSON或者其他任何基于文本格式的文档。模板引擎使用Groovy作为表达式语言。标签系统让你可以创建可复用功能。
模板保存在app/views
目录下。
模板文件是一个纯文本文件,其中有一部分作为动态生成内容的占位符。模板的动态元素使用Groovy语言,Groovy的语法与Java很相近。
动态元素在模板执行时被解析。渲染出的结果作为HTTP响应的一部分发送出去。
产生一个动态元素最简单的方法就是声明一个表达式。这里用到的语法是${...}
。对表达式求值以后的结果会插入表达式所在的位置。
例如:
<h1>Client ${client.name}</h1>
如果你不确定client是不是null,这里有一个Groovy的简便方法:
<h1>Client ${client?.name}</h1>
这样只会在client不为null的情况下才显示client的name。
装饰器提供了一种干净的解决方法在多个模板间共享页面布局(或设计)。
使用#{get}和#{set}标签在模板和装饰器间共享变量。
把页面嵌入一个装饰器只要一行代码就可以搞定:
#{extends 'simpledesing.html' /}
#{set title:'A decorated page' /}
This content will be decorated.
装饰器simpledesign.html
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>#{get 'title' /}</title>
<link rel="stylesheet" type="text/css" href="@{'/public/stylesheets/main.css'}" />
</head>
<body>
<h1>#{get 'title' /}</h1>
#{doLayout /}
<div class="footer">Built with the play! framework</div>
</body>
</html>
一个标签其实就是一段可以加参数调用的代码片段。如果标签只有一个参数,根据约定它被称为“arg”,而它的名称就可以省略。
例如,这个标签会插入一个用来加载JavaScript的SCRIPT标签:
#{script 'jquery.js' /}
标签必须关闭,要么直接关闭或者用一个结束标签:
#{script 'jquery.js' /}
或
#{script 'jquery.js'}#{/script}
举例来说list标签可以用来迭代一个集合,它需要两个必选的参数:
<h1>Client ${client.name}</h1>
<ul>
#{list items:client.accounts, as:'account' }
<li>${account}</li>
#{/list}
</ul>
所有动态的表达式都会经模板引擎转义以防止在你的应用里出现XSS安全问题。所以内容为<h1>Title</h1>
的title
变量会被转义:
${title} --> <h1>Title</h1>
如果你真的想要不转义的显示它,你需要显示调用raw()
方法:
${title.raw()} --> <h1>Title</h1>
并且,如果你想显示一大块原始HTML,你可以使用#{verbatim}标签:
#{verbatim}
${title} --> <h1>Title</h1>
#{/verbatim}
你可以使用路由从一条对应的路由规则来(逆向)生成URL。在模板中,你可以使用@{...}
语法来做这件事。
例如:
<h1>Client ${client.name}</h1>
<p>
<a href="@{Clients.showAccounts(client.id)}">All accounts</a>
</p>
<hr />
<a href="@{Clients.index()}">Back</a>
@@{...}
语法作用相同,不过生成的是一个绝对URL(尤其对e-mail很有用)。
如果你的应用需要国际化,你可以使用&{...}
语法来显示一条国际化消息。
例如在文件conf/messages
中我们指定:
要在模板中显示这条消息,只需:
<h1>&{'clientName', client.name}</h1>
注释不会被模板引擎求值,他们只是注释而已…
*{**** Display the user name ****}*
<div class="name">
${user.name}
</div>
脚本是一组复杂的表达式。一段脚本可以声明一些变量还有定义一些语句。使用%{}%
语法来插入一段脚本。
%{ fullName = client.name.toUpperCase()+' '+client.forname; }%
<h1>Client ${fullName}</h1>
一段脚本可以使用`out`对象直接写入动态内容:
%{
fullName = client.name.toUpperCase()+' '+client.forname;
out.print('<h1>'+fullName+'</h1>');
}%
你可以使用脚本来创建一个结构,比如在你的模板中创建一个迭代结构:
<h1>Client ${client.name}</h1>
<ul>
%{
for(account in client.accounts) {
}%
<li>${account}</li>
%{
}
}%
</ul>
请牢记模板不是一个处理复杂逻辑的地方,所以尽可能使用标签,或者把计算放到控制器或模型对象中去做。
一个模板可以继承另外一个模板,也就是说它可以作为一另一个模板的一部分被包含进去。
要继承另外一个模板,请使用`extends`标签:
#{extends 'main.html' /}
<h1>Some code</h1>
这个`main.html`模板是一个变种模板,但它使用`doLayout`标签来包含内容:
<h1>Main template</h1>
<div id="content"> #{doLayout /} </div>
你可以很方便的为你的应用创建一个特定的标签。一个标签其实就是个简单的模板文件,保存在app/views/tags
目录下。模板的文件名就作为标签名。
要创建一个hello
标签,只需要创建app/views/tags/hello.html
文件。
Hello from tag!
不需要进行任何配置。你可以直接使用这个标签:
#{hello /}
标签参数作为模板变量展现出来,变量名由参数名前面加上_
组成。
例如:
Hello ${_name} !
然后你可以把命名参数传递给标签:
#{hello name:'Bob' /}
如果你的标签只有一个参数,你可以使用的默认参数名是arg
而且它的名字是隐含的。
例如:
Hello ${_arg}!
然后你可以像这样方便地调用它:
#{hello 'Bob' /}
如果你的标签提供了一个body
,你可以在你标签代码的任意一点使用doBody
标签。
例如:
Hello #{doBody /}!
然后你可以把名字作为标签体传进来:
#{hello}
Bob
#{/hello}
你可以拥有不同版本的标签对应不同的"文档类型":http://www.playframework.org/documentation/1.2.4/routes#content-types ,然后Play会选择合适的标签。例如,Play会在request.format
是html
的时候使用app/views/tags/hello.html
标签,而当格式为xml
的时候使用app/views/tags/hellow.xml
。
无论文档类型是怎样的,当指定格式的标签不存在的时候,Play会退而求其次使用.tag
后缀的标签,比如app/views/tags/hello.tag
。
你还可以用Java代码来定义标签。类似JavaExtensions的机制,需要继承`play.templates.JavaExtensions`类,要创建一个FastTag你需要在一个继承了`play.templates.FastTags`的类中写一个方法。每一个你想要做为一个标签调用的方法必须有下面这样的方法签名。
public static void _tagName(Map<?, ?> args, Closure body, PrintWriter out,
ExecutableTemplate template, int fromLine)
注意标签名前面的下划线。
为了帮助理解如何创建一个实际的标签,让我们来看两个内置标签。
例如,verbatim
标签是由一行代码的方法实现的,它调用了JavaExtensions的toString方法,然后传入标签体。
public static void _verbatim(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
out.println(JavaExtensions.toString(body)); }
标签体可以是开始和结束标签之间的任何东西。那么
<verbatim>My verbatim</verbatim>
标签体的值就是
My verbatim
第二个例子是option
标签,稍微复杂一些因为它依赖一个父标签来发挥作用。
public static void _option(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
Object value = args.get("arg"); Object selection = TagContext.parent("select").data.get("selected"); boolean selected = selection != null && value != null && selection.equals(value);
out.print("<option value=\"" + (value == null ? "" : value) + "\" " + (selected ? "selected=\"selected\"" : "") + "" + serialize(args, "selected", "value") + ">"); out.println(JavaExtensions.toString(body)); out.print("</option>"); }
这段代码运行的时候会输出一个HTML的`option`标签,而且会通过检查符标签的哪个值被选中来设置选中值。前三行设置输出用的变量。然后,最后三行输出标签的结果。
在内置标签的源码中还有更多的例子,复杂程度不一而足。参见"FastTags.java in github":https://github.com/playframework/play/blob/master/framework/src/play/templates/FastTags.java
为了确保你的标签不会和项目间,或者核心的Play标签发生冲突,你可以创建命名空间,使用类级别的注解@FastTags.Namespace
。
于是,对于在命名空间my.tags
下的hello标签,你需要这样做
@FastTags.Namespace("my.tags")
public class MyFastTag extends FastTags {
public static void _hello (Map<?, ?> args, Closure body, PrintWriter out,
ExecutableTemplate template, int fromLine) {
...
}
}
然后在你的模板中,你可以这样引用hello标签
#{my.tags.hello/}
当你在模板引擎中使用你的java对象时,加进了一些新的方法。这些方法并不存在于原始的java类中,而是由模板引擎动态的加入进来。
例如,为了在模板中简单的使用数字格式化,向java.lang.Number
添加了一个format
方法。
然后就可以很轻松的格式化一个数字:
<ul>
#{list items:products, as:'product'}
<li>${product.name}. Price: ${product.price.format('## ###,00')} €</li>
#{/list}
</ul>
这同样适用于java.util.Date
。
在Java扩展手册中按类型列举了可用的方法。
你的项目可能会特定的格式需求,在这种情况下你可以提供你自己的扩展。
你只需要创建一个继承`play.templates.JavaExtensions`的Java类。
举例来说,给数字提供一个自定义货币格式器:
package ext;
import play.templates.JavaExtensions;
public class CurrencyExtensions extends JavaExtensions {
public static String ccyAmount(Number number, String currencySymbol) { String format = "'"+currencySymbol + "'#####.##"; return new DecimalFormat(format).format(number); }
}
每一个扩展方法都是一个静态方法,并且需要返回一个`java.lang.String`来写入到页面中。第一个参数会保持要加强的对象。
像这样来使用你的格式器:
<em>Price: ${123456.324234.ccyAmount()}</em>
模板扩展类在Play启动的时候会自动检测到。你只需要重启你的应用使他们变得可用。
所有加入到renderArgs作用域的对象会作为模板变量直接注入。
举个例子,要一个`user`bean从控制器注入到模板中:
renderArgs.put("user", user );
当你从一个动作渲染模板时,框架还会添加一些隐含对象:
变量 | 说明 | API文档 | 参见 |
---|---|---|---|
errors |
验证错误 | play.data.validation.Validation.errors() | 验证HTTP数据 |
flash |
Flash作用域 | play.mvc.Scope.Flash | 控制器 – Session和Flash作用域 |
lang |
当前语言 | play.i18n.Lang | 设置国际化 – 定义语言 |
messages |
消息map | play.i18n.Messages | 设置国际化 – 外部化消息 |
out |
输出流写入器 | java.io.PrintWriter | |
params |
当前参数 | play.mvc.Scope.Params | 控制器 – HTTP参数 |
play |
主框架类 | play.Play | |
request |
当前的HTTP请求对象 | play.mvc.Http.Request | |
session |
Session作用域 | play.mvc.Scope.Session | 控制器 – Session和Flash作用域 |
除了上面列出的名称之外owner
、delegate
和it
是在Groovy中保留使用的,而且不应该作为模板中的变量名来使用。