テンプレートエンジン
Play には、HTML、XML、JSON、その他あらゆるテキストベースのドキュメントを動的に生成する効率的なテプレートシステムがあります。テンプレートエンジンは式言語として Groovy を使用します。タグシステムによって、再利用可能な機能を作成することができます。
テンプレートは app/views ディレクトリに保存されます。
テンプレートの構文
テンプレートファイルは、一部に動的に内容を生成するためのプレースホルダを持つテキストファイルです。テンプレートの動的な要素は、 Groovy 言語を使用して書かれます。Groovy の構文は Java のそれにとてもよく似ています。
動的な要素はテンプレートの実行中に解決されます。レンダリング結果は、HTTP レスポンスの一部として送信されます。
Expressions: ${…}
動的な要素を作る最もシンプルな方法は、式を宣言することです。ここで使用する構文は ${…} です。この式の評価結果は、この式部分に挿入されます。
例えば、以下のようにします:
<h1>Client ${client.name}</h1>
client が null でないと確信できない場合には、Groovy のショートカットがあります:
<h1>Client ${client?.name}</h1>
これは client が null でない場合にのみ、client の name を表示します。
テンプレートデコレータ : #{extends /} と #{doLayout /}
デコレータは複数テンプレートに渡ってページのレイアウト (またはデザイン) を共有するきれいなソリューションを提供します。
テンプレートとデコレータの間で変数を共有するには、 #{get} および #{set} タグを使用してください。
デコレータにページを埋め込むことは一行で行えます。
#{extends 'simpledesign.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 /}
タグは、パラメータと共に呼ぶことができるテンプレートの断片です。タグに 1 つのパラメータしかない場合、規約によりそのパラメータは “arg” と呼ばれ、省略することができます。
例えば、このタグは JavaScript ファイルをロードするための SCRIPT タグを挿入します:
#{script 'jquery.js' /}
タグは直接閉じるか、終了タグによって閉じられなければなりません:
#{script 'jquery.js' /}
または、以下のようにします。
#{script 'jquery.js'}#{/script}
例えば、 list タグはどんなコレクションでもくり返すことができます。list タグは 2 つの必須パラメータを受け取ります:
<h1>Client ${client.name}</h1>
<ul>
#{list items:client.accounts, as:'account' }
<li>${account}</li>
#{/list}
</ul>
すべての動的な式は、アプリケーションの XSS セキュリティ問題を回避するために、テンプレートエンジンによってエスケープされます。そのため <h1>Title</h1> を含む titile 変数はエスケープされてしまいます:
${title} --> <h1>Title</h1>
エスケープせずに表示したい場合は、明示的に raw() メソッドをコールする必要があります:
${title.raw()} --> <h1>Title</h1>
素の HTML のかなりの部分を表示したい場合は、 #{verbatim /} タグを使用することができます:
#{verbatim}
${title} --> <h1>Title</h1>
#{/verbatim}
アクション: @{…} または @@{…}
Router を使用することで、指定されたルートに対応する URL を (リバースして) 生成することができます。特別な @{…} 構文を使用して、テンプレートからこれを行うことができます。
例えば、以下のようにします:
<h1>Client ${client.name}</h1>
<p>
<a href="@{Clients.showAccounts(client.id)}">All accounts</a>
</p>
<hr />
<a href="@{Clients.index()}">Back</a>
@@{…} 構文は、(とりわけ email, … に便利な) 絶対 URL を生成する点以外は同じです。
メッセージ: &{…}
アプリケーションを国際化する必要がある場合は、 &{…} 構文を使って国際化されたメッセージを表示することができます:
例えば 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} !
そして、この name パラメータをタグに渡すことができます:
#{hello name:'Bob' /}
タグにパラメータが 1 つしかない場合、暗黙的な名前である arg というデフォルトパラメータ名を使うことができます。
例えば:
Hello ${_arg}!
次のようにして、これを容易に使用することができます:
#{hello 'Bob' /}
タグボディの実行
タグが body をサポートする場合、 doBody タグを使用することでタグコードの任意の位置にボディの内容を取り込むことができます。
例えば、以下のようにします:
Hello #{doBody /}!
そして、タグボディをこの名前として渡すことができます:
#{hello}
Bob
#{/hello}
カスタム Java タグ
Java コードでカスタムタグを定義することも可能です。Java エクステンションが play.templates.JavaExtensions クラスを継承して動作するのと同じように、FastTag を作るためには play.templates.FastTags を継承するクラスにメソッドを作成する必要があります。タグとして実行するいずれのメソッドも、以下のメソッドシグネチャに従わなければなりません。
public static void _tagName(Map<?, ?> args, Closure body, PrintWriter out,
ExecutableTemplate template, int fromLine)
タグ名の前のアンダースコアに注意してください。
実際のタグの作り方を理解するために、二つの組み込みタグを見てみましょう。
例えば、 verbatim タグは単純にタグボディを引き渡し、 Java エクステンション上で 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 タグを出力し、親タグでどの値が選択されたかを確認して selected 値を設定します。最初の三行は出力に使う変数を設定します。その後、最後の三行でこのタグの結果を出力します。
組み込みタグのソースコードに、様々な度合いの複雑さを持った多くのサンプルがあります。github の FastTags.java を参照してください。
タグの名前空間
タグが、プロジェクト間、または Play の中心的なタグと衝突しないことを保証するために、クラスレベルのアノテーション @FastTags.Namespace を使って名前空間を設定することができます。
以下のようにして hello タグの名前空間を my.tags にします。
@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 にも同様に適用することができます。
型ごとに利用できるメソッドの一覧は API ドキュメント で見つけることができます。
独自拡張の作成
プロジェクトが、特別なフォーマットを必要とする場合、独自の拡張を提供することができます。
必要なのは 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);
}
}
拡張メソッドは static なメソッドであり、ページに書き戻すために 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 | フラッシュスコープ | play.mvc.Scope.Flash | コントローラ - セッションとフラッシュのスコープ |
lang | 現在の言語 | play.i18n.Lang | I18N の設定 - 言語の定義 |
messages | メッセージのマップ | play.i18n.Messages | I18N の設定 - メッセージの外部化 |
out | 出力ストリーム | java.io.PrintWriter | |
params | 現在のパラメータ | play.mvc.Scope.Params | コントローラ - HTTP パラメータ |
play | フレームワークの中心クラス | play.Play | |
request | 現在の HTTP リクエスト | play.mvc.Http.Request | |
session | セッションスコープ | play.mvc.Scope.Session | コントローラ - セッションとフラッシュのスコープ |