#基本は変数式とOGNL
Thymeleafの基本は、「値を出力する(表示する)」ということです。これは、${{○○}}という形で記述されます。この${0}という書き方は「変数式」と呼ばれます。変数式の中に記述されるのは、「OGNL」(Object-Graph Navigation Language)式という、Javaの値にアクセスするための式言語です。Thymeleafに限らず、各種のライブラリやフレームワークなどで使われています。
OGNLは、Javaの簡易版のような書き方をするので、Javaプログラマであればそれほど難しくはありません。基本的に「Javaで式を書けば、シンプルなものならたいていはOGNLの式になる」と考えてしまっていいでしょう。既に基本的なOGNLは使っていますが、もう少し違ったサンプルを挙げてみましょう。
<body>タグの部分をこのように書き換えてみてください。
リスト4-1
<body>
<h1>Helo page</h1>
<p th:text="${new java.util.Date().toString()}"></p>
</body>
ここでは、th:textに、${new java.util.Date().toString()}というように値を指定しています。変数式には、単に変数や値などを書くだけでなく、このようにインスタンスをnewしたり、メソッドを呼び出したりするOCNLの式を書くこともできるのです。
#ユーティリティオブジェクト
変数式は、その名の通り、変数を記述してそのまま出力することができます。この変数は、既に簡単なサンプルで動作を確認したように、まずコントローラーで値を用意しておき、それをテンプレート側に出力する、というのが基本でした。
が、例えばJavaのクラスなどの中には、こうしたテンプレートで頻繁に用いられるクラスもあります。そうしたものを利用する際、常に「コントローラーでこのクラスを変数として用意して……」とやるのはかなり面倒くさいものですね。そこでThymeleafでは、よく使われるクラスを「#名前」という定数として変数式の中に直接記述して利用できるようにしました。これが「ユーティリティオブジェクト」です。ユーティリティオブジェクトには、以下のようなものがあります。
ユーティリティオブジェクト | 概要 |
---|---|
#strings | Stringクラスの定数です。 |
#numbers | Numberクラスの定数です。 |
#bools | Booleanクラスの定数です。 |
#dates | Dateクラスの定数です。 |
#objects | Objectクラスの定数です。 |
#arrays | Arrayクラスの定数です。 |
#lists | Listクラスの定数です。 |
#sets | Setクラスの定数です。 |
#maps | Mapクラスの定数です。 |
これらはクラスの定数ですので、ここから直接クラスメソッドなどを呼び出して利用することができます。ただし、例えば「new #dates」などとやってDateインスタンスを作る、といった使い方はできません。あくま「#dates.○○」というように、クラスフィールドやクラスメソッドの呼び出しなどを行うのに利用するもの、と考えてください。
<body>
//日時の整形
<p th:text="$£#dates.format(new java.util.Date(), 'dd/MMM/yyyy HH:mm')}"></p>
//整数の整形
<p th:text="${#numbers.formatInteger(1234,7)}"></p>
//テキストの整形
<p th:text="${#strings.toUpperCase('Welcome to Spring.')}"></p>
</body>
#パラメータへのアクセス
Webアプリケーションでは、クエリ文字列にパラメーターを付けて送信し、取得することがありますが、コントローラーを通さずに、直接テンプレート内でパラメーターを利用することができます。
この場合に使用するのが「param」変数です。これは、変数式の中で直接利用できる変数です。変数の中からパラメーター名を指定して値を取り出すことができます。通常はこうして得られる値は配列となっています。配列とすることで、同じ値を複数指定することがでるのです。
URLをhttp://localhost:8080/?id=123&name=taro
とパラメータを指定すると、123
とtaro
の値を取り出すことができます。
<body>
<h1>テストページ</h1>
<p th:text="'取得したパラメータ:id=' + ${param.id[0] + ',name=' + param.name[0]}"></p>
</body>
####テキストリテラルの記述
ここで記述しているth:textの値を見ると、ダブルクォートの中に、更にシングルクォートで値が記述されていることがわかります。これは、OGNLでテキストリテラルを記述する際の書き方です。1つのテキストリテラルをそのまま表示する場合は、ダブルクォート内に直接テキストを書けばいいのですが、いくつかのリテラルをつなぎ合わせるような場合は、ダブルクォート内に更にシングルクォートでテキストリテラルを記述することができます。
th:text="one two three"
th:text=""one ' + 'two ' + 'three!"
上記の例文は、どちらも同じ「one two three」というテキストを出力しますが、後者はそれぞれの単語を+記号でつなぎ合わせるようにしていますね。こんな具合に、ダブルクォートの中で更に式を使って値を組み立てられるのですね。
####配列は何のため?
paramの値を利用するとき、「配列の最初の要素を取り出して使う」と説明しました。が、「そもそも、なんで配列なの?」と思った人も多いかもしれませんね。これは、クエリーテキストとして値を送信するとき、同じ名前の値を複数送る場合に対応できるようにするためです。「同じ名前の値を複数送る」というのはわかりにくいでしょうが、例えばこういうことです。
http://localhost:8080/?id=123&id=456&name=tuyano&name=hanako
こんな具合にすると、paramから取り出されるid とnameの値は、それぞれ123,456)、"tuyano", "hanako")となります。後は、ここから配列内のそれぞれの値を取り出して処理すればいい、というわけです。
#メッセージ式
${}という変数式以外にも、Thymeleafでは利用できるものがあります。「メッセージ式」です。
これは、プロジェクトにあらかじめ用意しておいたプロパティファイルから値を取り出し、表示します。Javaでは、プロパティファイルにあらかじめテキストをまとめておき、それを読み込んで利用することがよくあります。メッセージ式は、これをテンプレート内から直接利用できるようにしたものといえるでしょう。
メッセージ式は、以下のように記述します。
#{ 値の指定 }
${}ではなく、__#{}__という形で記述するのが特徴です。 { }内には、取り出す値を指定します。これは、プロパティファイルに記述する値に応じて記述します。
resourcesフォルダ内に「messages.properties」というテキストファイルを作成し、以下を記入します。
content.title=message sample page.
content.message=this is sample message from properties.
htmlを以下のように記述することで、「messages.properties」のmessage sample page.
とthis is sample message from properties.
の値を表示することができます。
<body>
<h1 th:text="#{content.title}">Hello page</h1>
<p th:text="#{content.message}"></p>
</body>
#リンク式とhref
Webページでは、URLを指定したリンクもさまざまなところで利用されます。リンクを指定する専用の式が用意されています。
@{アドレス}
これは基本的にURLを指定するような属性(aタグなど)で利用します。
リンク式を利用するメリットは、ほかの変数などと組み合わせてリンクのアドレスを指定することができることです。
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<p><a th:href="a{ '/home/{id}'(id=${param.idc0]})}">link</a></p>
</body>
アクセスする際にクエリーテキストにidを用意します。例えば、http://localhost:8080/?id=123
というようにアクセスすると、リンクには/home/123というように設定されます。
#選択オブジェクトへの変数式
変数式は、基本的にコントローラー側に用意した値をそのまま出力するだけのものですが、これは数値やテキストといったシンプルな値ならばいいのですが、オブジェクトを利用するようになると扱いに困ってしまいます。
例えば${object.name}
といった具合にオブジェクトのプロパティやメソッドを指定して書けばいいですが、オブジェクト名が変更されたりしたときにすべての名前をまた書き換えないといけないのは大変です。このような場合、オブジェクトを指定し、その選択されたオブジェクト内の値を取り出すための専用の変数式が *{}
です。
たぐにth:object
という属性を使ってオブジェクトを設定します。こうすることで、そのタグの内部で*{}
が使えるようになります。
<div th:object="${オブジェクト}">
<p th:text="*(プロパティ}"></p>
</div>
@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("msg","current data.");
Dataobject obj = new DataObject(123, "hanako","hanako@flower");
mav.addObject("object",obj);
return mav;
}
class DataObject{
private int = id;
private String = name;
private String = email;
public DataObject(int id,String name,String email){
super();
this.id = id;
this.name = name;
this.email = email;
}
public int getId(){return id;}
public void setId(int id){this.id = id;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public String getEmail(){return email;}
public void setEmail(String Email){this.email = email;}
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<p th:text="${msg}">message.</p>
<table th:object="${object}">
<tr><th>ID</th><td th:text="*{id}"></td></tr>
<tr><th>NAME</th><td th:text="*{name}"></td></tr>
<tr><th>MAIL</th><td th:text="*{email}"></td></tr>
</table>
</body>
#リテラル置換
変数式の中にいくつかの値を組み合わせてテキストを出力させる場合、"'A'+'B'"
といいうように、ダブルクオート内にさらにテキストリテラルをつなぎ合わせて書いてきましたが、リテラル置換では"|テキストの内容|"
とすることで、この中に直接数式を書き込むことができます。
@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("msg","current data.");
Dataobject obj = new DataObject(123, "hanako","hanako@flower");
mav.addObject("object",obj);
return mav;
}
class DataObject{
private int = id;
private String = name;
private String = email;
public DataObject(int id,String name,String email){
super();
this.id = id;
this.name = name;
this.email = email;
}
public int getId(){return id;}
public void setId(int id){this.id = id;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public String getEmail(){return email;}
public void setEmail(String Email){this.email = email;}
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<div th:object="${object}">
<p th:text="|my name is *{name}". mail address is *{email}.|">massage</p>
</div>
</body>
#HTMLの出力
Thymeleafでは変数式でテキストを出力する際、安全のためにHTMLタグをすべてエスケープするようになっています。
そのため、HTMLの出力ではth:text="${}"
ではなく、__th:utext="${}"
__を使用します。
th:utext="${}"
はアンエスケープテキスト(エスケープしないテキスト)を出力する属性です。
@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
mav.addObject("msg","massage1<br/>massage2");
return mav;
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<p th:utext="${msg}">massage.</p>
</body>
#条件式
真偽値がtrueの場合とfalseの場合で表示する値を選ぶことができます。
"真偽値 ? true値 : false 値"
取得したパラメーター値をid % 2 == 0
で真偽値を判定し、それに応じた値を表示する例を作ります。
http://localhost:8080/1000
// import org.springframework.web.bind.annotation.PathVariable;
@RequestMapping("/{id}")
public ModelAndView index(@PathVariable int id,MdelAndView mav) {
mav.setViewName("index");
mav.addObject("id",id);
mav.addObject("check",id % 2 == 0);
mav.addObject("trueVal","Even number");
mav.addObject("falseVal","Odd number");
return mav;
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<p th:utext="${id} + 'is' + (${check} ? ${trueVal} * ${falseVal})"></p>
</body>
#条件分岐
条件式は「真偽値を使って2つの値のどちらかを表示する」というものでしたが、表示そのものをON/OFFすることもできます。
trueの場合表示
th:if="条件"
falseの場合表示
th:unless="条件"
これらの属性の特徴は、true/falseの判定が、__真偽知だけでない__と言うことです。
- trueと表示されるもの
- ・true値
- ・ゼロ以外の数値
- ・0、off、no、といった値以外のテキスト
- falseと表示されるもの
- ・false値
- ・数値のゼロ
- ・0、off、no、といった値のテキスト
http://localhost:8080/1000
// import org.springframework.web.bind.annotation.PathVariable;
@RequestMapping("/{id}")
public ModelAndView index(@PathVariable int id,MdelAndView mav) {
mav.setViewName("index");
mav.addObject("id",id);
mav.addObject("check",id % 2 == 0);
mav.addObject("trueVal","Even number");
mav.addObject("falseVal","Odd number");
return mav;
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<p th:if="${check} th:text="${id} + 'is' + ${trueVal}"></p>
<p th:unless="${check} th:text="${id} + 'is' + ${falseVal}"></p>
</body>
#多項分岐「th:switch」
< div th:switch="条件式">
<p th:case="条件式"></p>
<p th:case="条件式"></p>
</div>
クエリーテキストで渡された数字によって季節を判定する例を作ってみます。
@RequestMapping("/{month}")
public ModelAndView index(@PathVariable int month,MdelAndView mav) {
mav.setViewName("index");
mav.addObject("month",month);
mav.addObject("check",month);
return mav;
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
< div th:switch="${check}">
<p th:case="0" th:text="|${month} - Winter|"></p>
<p th:case="1" th:text="|${month} - Spring|"></p>
<p th:case="2" th:text="|${month} - Summer|"></p>
<p th:case="3" th:text="|${month} - Autumn|"></p>
<p th:case="*">....?</p>
</div>
</body>
#繰り返しの「th:each」
${コレクション}
から順に値を取り出し、${変数}
に代入していきます。
< div th:each="変数 : ${コレクション}">
....${変数} を利用した表示....
</div>
//import java.util.ArrayList;
@RequestMapping("/")
public ModelAndView index(MdelAndView mav) {
mav.setViewName("index");
ArrrayList<String[]> data = new ArrayList<String[]>;
data.add(new String[]{"taro","taro@sample.com","00000000"});
data.add(new String[]{"hanako","hanako@sample.com","00000000"});
data.add(new String[]{"sachiko","sachiko@sample.com","00000000"});
mav.addObject("data",data);
return mav;
}
<body>
<h1 th:text="#{content.title}">Helo page</h1>
<table>
<tr>
<th>NAME</th>
<th>MAIL</th>
<th>TELL</th>
</tr>
<tr th:each="obj:${data}">
<td th:text="${obj[0]}"></td>
<td th:text="${obj[1]}"></td>
<td th:text="${obj[2]}"></td>
</tr>
</table>
</body>
#th:ftagment
HTMLを部品化するための属性。部品化されたHTMLはフラグメントと呼ぶ。
th:fragment属性を利用して定義したフラグメントは、th:fragment="フラグメント名 (param1,param2)"のようにパラメータを取ることが可能である。
パラメータを取ることでフラグメントの汎用性を高めることが可能となり、HTML部品の共通化が容易となる。
パラメータ化されたフラグメントを取得する場合は、~{Viewのパス :: フラグメント名 (${address},true)}のようにフラグメント式のフラグメント名の後ろに、パラメータとして渡す値を記述する。
パラメータとして渡す値には、変数式(${...})やリテラル(文字列、真偽値など)のほか、フラグメント式(~{...})を指定することも可能である。
パラメータを渡す際に、~{Viewのパス :: フラグメント名 (param1=${address},param2=true)}のようにパラメータ名を明示的に記述することも可能である。 この場合、パラメータを渡す順序はフラグメントで定義したパラメータの順序と異なっていても問題ない。
#th:insert
フラグメントを参照するための属性。
指定したタグに参照したフラグメントを挿入することができる。指定したタグ自体は残るが、そのタグの子要素は残らない。
#th:replace
フラグメントを参照するための属性。
指定したタグを参照したフラグメントで置換することができる。指定したタグ自体も残らない。
#フラグメント式
HTMLの部品化と関連して、フラグメント式という機能がある。
フラグメント式は変数式などと同様にテンプレートHTML内で使用することができ、テンプレートHTMLの断片を取得することができる。
th:insert 属性、th:replace 属性と組み合わせて使うことで、フラグメントやテンプレートHTMLの一部を他のテンプレートHTMLに埋め込むことができる。
式 | 説明 |
---|---|
~{Viewのパス :: セレクタ} | Viewのパス で指定されたテンプレートHTMLから、指定されたセレクタ に該当する要素を特定し取得する。 |
~{Viewのパス :: フラグメント名} | Viewのパス で指定されたテンプレートHTMLから、指定されたフラグメント名 に一致するth:fragment 属性が設定された要素を特定し取得する。 |
~{Viewのパス} | Viewのパス で指定されたテンプレートHTML全体を取得する。 |
~{:: セレクタ} | フラグメント式が記載されているテンプレートHTML自体から、指定されたセレクタ に該当する要素を特定し取得する。 セレクタ の代わりにフラグメント名 を指定することも可能である。 |
~{this :: セレクタ} | ~{:: セレクタ}のべつの記載方法 |