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。

模板装饰器:#{extends /}和#{doLayout /}

装饰器提供了一种干净的解决方法在多个模板间共享页面布局(或设计)。

使用#{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>

标签:#{tagName /}

一个标签其实就是一段可以加参数调用的代码片段。如果标签只有一个参数,根据约定它被称为“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} --> &lt;h1&gt;Title&lt;/h1&gt;

如果你真的想要不转义的显示它,你需要显示调用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中我们指定:

clientName=The client name is %s

要在模板中显示这条消息,只需:

<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.formathtml的时候使用app/views/tags/hello.html标签,而当格式为xml的时候使用app/views/tags/hellow.xml

无论文档类型是怎样的,当指定格式的标签不存在的时候,Play会退而求其次使用.tag后缀的标签,比如app/views/tags/hello.tag

自定义Java标签

你还可以用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类中,而是由模板引擎动态的加入进来。

例如,为了在模板中简单的使用数字格式化,向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作用域

除了上面列出的名称之外ownerdelegateit是在Groovy中保留使用的,而且不应该作为模板中的变量名来使用。

⚠️ **GitHub.com Fallback** ⚠️