はじめに
以前に書いた記事の続編です。
「thymeleaf enum」「thymeleaf constant」で検索すると、 T(my.package.MyEnum).MONDAY
, ${T(com.baeldung.thymeleaf.model.Color).values()}
, T(com.example.Constants).MIN_VALUE
といった、thymeleafから直接呼び出すような形の記事がでてくることが多いです。
では、この使い方が公式ドキュメントにあるものかというと、そうではないです。
T(
で検索をかけても、上記の使い方に対応するものはヒットしないです。
この書き方は、特に考えずにすぐ動くものができるので、一見良さそうですが、保守性や可読性からみると良いといえないです。このやり方に頼らない、別の方法をまとめるのがこの記事の趣旨です。
結論
- controllerのメソッド内でmodelAndViewにわたす
- controllerでModelAttributeを定義する
- Beanに登録する
- インスタンスメソッドに委譲し、テンプレートでそれを呼び出す
T(com.example.Constants)のやり方の問題点
このやり方は、パッケージ構成やクラス名の変更が、テンプレート側にも影響してしまいます。
IDEのリファクタリング機能では、テンプレートの方までは変えてくれないので、安全にリファクタリングできなくなってしまいます。
テンプレートエンジンの基本は、「テンプレート内の変数に代入」です。テンプレートから直接アプリのコードを呼び出すのは、アプリとテンプレートを結合度を不要に高めてしまいます。
サンプルコード
public enum UserStatus {
ACTIVE("有効"),
INACTIVE("無効"),
PENDING("保留");
private final String viewName;
...
}
public class UserConstants {
public static final int MAX_PAGE_SIZE = 100;
public static final int MIN_PAGE_SIZE = 10;
}
controllerのメソッド内でmodelAndViewにわたす
一番オーソドックスなやり方です。
enumの要素をfor-eachする場合は、Enum#values()をModelに渡します。
定数についても同様です。
@Controller
public class UserController {
// 更新画面の表示
// ステータスのセレクトボックスを表示
public String updateView(ModelAndView model, UserUpdateForm form) {
...
model.addAttribute("statusList", UserStatus.values());
model.addAttribute("maxPageSize", UserConstants.MAX_PAGE_SIZE);
...
}
}
<select name="status">
<option th:each="status : statusList}"
th:value="${status}" th:text="${status.viewName}">
</option>
</select>
<div th:text=${maxPageSize}>
controllerでModelAttributeを定義する
ひとつのcontroller内で扱う複数の画面で、使われる場合です。
@ModelAttribute
は、controller内の各メソッドが呼び出される前に実行されます。
テンプレートでの使い方は通常どおりです。
@Controller
public class UserController {
@ModelAttribute("activeStatus")
public UserStatus active() {
return UserStatus.ACTIVE;
}
...
}
<input name="status" th:value="${activeStatus}" th:text="${activeStatus.viewName}">
Beanに登録する
複数のcontrollerが扱う画面で使う場合です。それぞれのcontrollerで@ModelAttribute
を使う手もありますが、@Bean
を使えば一箇所ですみます。
テンプレートでの使い方も、通常どおりです。
@Configuration
public class Configs {
@Bean("userActiveStatus")
public UserStatus activeStatus() {
return UserStatus.ACTIVE;
}
}
<input name="status" th:value="${userActiveStatus}" th:text="${userActiveStatus.viewName}">
インスタンスメソッドに委譲し、テンプレートでそれを呼び出す
冒頭にあげた、よくないやり方をつかえば、テンプレートからstaticメソッドを呼び出すこともできます。
public class User {
private final String name;
...
}
public class UserUtil {
public static String formatName(String name) {...}
}
<p th:text="${T(com.sample.UserUtil.formatName(user.name))}">
こういった場合は、インスタンスメソッドに移せば簡単に解決します。
public class User {
private final String name;
...
public final String getFormatedName() {
// UserUtil#formatNameの中身を移す
...
}
}
このように書き直せます。
<p th:text="${user.formatedName}">
まとめ
ModelAttribute,Beanや、controllerとテンプレートのやりとりの部分は、Springの知識が必要ですが、それ以外は、テンプレートエンジンの一般的な使い方と、thymeleafでの基本的な記法でできます。
上手にthymeleafと付き合っていきましょう。