記事のターゲット
- Thymeleafを実際に使っていていろんなところで詰まった人
目次
はじめに
今回の記事はThymeleafを使う上でいろいろ詰まりやすそうなところの解決法をまとめていきたいと思います。
Springでは推奨されているテンプレートエンジンなので使い方をマスターできると楽になる場面が多いと思います。
messages.propertiesに定義したメッセージをThymeleafで表示する方法
step
1まずはメッセージファイルを作成する
ファイル作成はどこでもいいのですが、わかりやすいように\src\main\resourcesの中にmessages.propertiesというファイルを作成します。
これがあることでSpringでは自動でメッセージを取得してくれるようになります。
step
2ファイル内にメッセージを定義する
好きに命名した名前=表示させたいメッセージ
という命名規則で定義していきます。
今後はイコールの左に記述した名前を使ってメッセージを表示させることができます。これをすることにより、メッセージを変更したいときの改修箇所がこのファイルだけで済むようになります。
step
3~Application.javaファイルを確認する
上の手順が終わってもSpringがメッセージの場所を見つけられない場合があります。その場合はmainメソッドのあるConfigurationクラスを確認します。
Springを使用する際特にいじったりしていなければ自動的に生成されるのですがたまに消えていたりするので以下の記述を追加します。
package example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
@Configuration
@SpringBootApplication
@ComponentScan
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
//無ければ追加↓
//---------------------------------------------------------------------------------------
@Bean
public MessageSource messageSource() {
//メッセージの場所を定義するメソッドを追加
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
//---------------------------------------------------------------------------------------
}
step
4Thymeleafに記述する
<h1 th:text="#{schedule.manage}"></h1>
あとはめちゃくちゃ簡単で、上記のように#{定義したメッセージ}という記述で表示させることができます。
下記実際に動かしてみます。
HTML上では文字を打ち込んでいないのですがきちんと「スケジュール管理」という文字が表示されるようになります。
ThymeleafにCSSやJSファイルを読み込む方法
HTMLファイルに直書きしても動きますが、普通外部ファイルにまとめて記述するのでそれを使うときには読み込みが必要となります。
そのやり方を紹介します。
step
1まずはファイルを作成する
フォルダパスはsrc/main/resources/static以下に置いてください。そうしないと読み込んでくれません。
step
2Thymeleafへ記述する
<!-- CSS -->
<link th:href="@{/css/table.css}" rel="stylesheet">
<!-- JS -->
<script th:src="@{/js/scheduleCreate.js}"></script>
それぞれ上記のように記述し、<head>タグ内に入れることで読み込んでくれます。
divと同じような使い方ができるth:block
//listが空ではなかった場合
<th:block th:if="!${#lists.isEmpty(list)}">
//何かしらの処理
</th:block>
<th:block>タグを使うことによりほかの要素に影響を及ぼさないブロックを作ることができます。
上記のコードの場合listが空だった場合はブロック内のすべてのコードを無視してくれるのでif文等でよく使います。
繰り返しで使えるth:each
step
1オーソドックスな1重ループ
<th:block th:each="formname : ${list名}">
//繰り返し処理
<p th:text="${formname.title}" />
</th:block>
th:each="days : ${list名}"のように記述します。daysの部分は自分で好きな名前を付けられます。イメージとしては拡張For文の左に記述する変数だと思ってください。
右のEL式(${})部分には回したいリストを指定します。そうすることでリストを回すことができます。
th:eachに囲まれた内側では取り出したリストに対して自分で決めた変数名を使ってアクセスします。今回の例でいうとformname部分です。${formname.title}のように.(ピリオド)を間に挟むことによってフィールドにアクセスできます。javaのフォームクラス等でよく見かけるgetterのような役割を果たします。
step
2Javaの基本For文に近い1重ループ
先ほどのコードだと、今が何回目の処理なのか等のステータスがわかりません。
Javaに慣れている方だとよく使う、
for(int i = 0; i < list.length(); i++){
}
のような繰り返しのiの部分を実現することができません。なぜならThymeleafの繰り返し文はデフォルトが拡張For文だからです。もしこれらを実装したい場合はステータス変数というものを使用します。
以下のように記述します。
<th:block th:each="formname, status : ${list名}">
//繰り返し処理
<th:block th:if="${status.even}">
<p th:text="${formname.title}" />
</th:block>
</th:block>
今回のソースは偶数回の処理時のみタイトルを出力するコードです。一番最初のループと違う部分は、
th:each="formname, status : ${list名}"
この部分です。コロンよりも左側に、
・使用したい変数
・ステータス保持用変数
の2つをカンマ区切りで記述することによってFor文でいうint i の部分を作り出すことができます。
また、ステータス変数には便利なプロパティが用意されています。下の表にまとめます。
ステータス変数の後につけるプロパティ | 意味 |
---|---|
index | 繰り返しの回数(0開始) |
count | 繰り返しの回数(1開始) |
size | 繰り返し対象の総件数 |
current | 現在の処理で使っている要素値 |
odd | 奇数回かどうかの判断に使う |
even | 偶数回かどうかの判断に使う |
first | 繰り返しが最初かどうかの真偽値 |
last | 繰り返しが最後かどうかの真偽値 |
「自分で決めたステータス変数名.プロパティ」でいろいろと応用が利きます。
step
3入れ子になったリストを表示する2重ループ
次はループの中にループを入れる方法を見ていきます。今まで通りの記述方法を2つ重ねるだけなので特に難しいことはありません。
例としてカレンダーの中に日付とその日に入っている予定を全て出力するものを書いてみます。
画面へ渡すForm
public class ScheduleIndexForm {
//予約種別の保持
private List<ScheduleTypeForm> scheduleTypeFormList;
//カレンダー作成に必要な情報保持
private Map<String, String> calendar;
//日付情報保持(日付、その日の予定のリスト)
private List<DayInfoForm> dayInfoFormList;
}
public class DayInfoForm {
private String day;
//予約情報
private List<ScheduleForm> scheduleFormList;
}
上記のように入れ子構造のリストがあります。
SpringのModelへ直接渡すのはScheduleIndexFormです。
Thymeleafの記述
<table>
<thead>
<tr>
<th>月</th>
<th>火</th>
<th>水</th>
<th>木</th>
<th>金</th>
<th>土</th>
<th>日</th>
</tr>
</thead>
<tbody>
<!-- まずはscheduleIndexFormの予約情報が入っているリストを回します。 -->
<th:block th:each="days , stat: ${scheduleIndexForm.dayInfoFormList}">
<!-- カレンダーを7日で折り返すようにループ回数が7の倍数の時に<tr>を挿入 -->
<th:block th:if="${stat.count % 7 == 1}">
<tr>
</th:block>
<td>
<!-- 日付を出力 -->
<p th:text="${days.day}" />
<!-- DayInfoFormの予約情報リストが空ではなかったら -->
<th:block th:if="!${#lists.isEmpty(days.scheduleFormList)}">
<!-- 予約情報が入っているリストを回す(2重目のループ) -->
<th:block th:each="schedule:${days.scheduleFormList}">
<p th:text="${schedule.title}" />
</th:block>
</th:block>
</td>
<th:block th:if="${stat.count % 7 ==0}">
</tr>
</th:block>
</th:block>
</tbody>
</table>
上記を実行するとカレンダーに日付を出力した後、予約が入っている場合のみそのすべてのタイトルをセルの中に順番に出力できます。
2つめのループでは「外側のループで定義した変数.プロパティ名」で要素にアクセスできます。
<th:block th:each="days , stat: ${scheduleIndexForm.dayInfoFormList}">
<th:block th:each="schedule:${days.scheduleFormList}">
<p th:text="${schedule.title}" />
</th:block>
</th:block>
今回でいうとこの部分です。外側で「days」として指定した変数を内側のループで「schedule」に値を入れるときに使っています。
まとめ
今後もThymeleaf関連でみつけたところがあれば更新していきたいと思います。