LoginSignup
16
20

thymeleafでenumを扱うためのtips

Last updated at Posted at 2019-12-03

今年になってSpring bootを使う案件に関わることがあり、thymeleafやenumについての得られた知見を整理してみました。

「Thymeleaf enum」でググるとよくでてくるもの

SpringやSpring bootを使うと、テンプレートエンジンでThymeleafを使うのが通例でしょう。
入力フォームのセレクトボックスの値や表示名を、enumを使って管理する、というのも、よくあることでしょう。

https://www.baeldung.com/thymeleaf-enums より抜粋ですが、ググると下記のような例がそこそこ見かけます。

<select name="color">
    <option th:each="colorOpt : ${T(com.baeldung.thymeleaf.model.Color).values()}"
        th:value="${colorOpt}" th:text="${colorOpt.displayValue}"></option>
</select>

初期値の判定などで、特定のenumかどうかを判定する場合の例として、こういうのもあります。

<div th:if="${widget.color == T(com.baeldung.thymeleaf.model.Color).RED}">

このやり方で難がある点

よく見かけるのですが、これがベストかと言われると、なんともいえないです。

  1. 型のFQDNの部分が読みづらい (長い割に情報量がない)
  2. パッケージ構成を見直そうとすると、テンプレートまで改修しなければならない
  3. enumのリネームでも、テンプレートまで改修しなければならない

要は、テンプレートとは関係ないものが入ってくるため、可読性や保守性に悪影響がでています。
とりあえず動くものを作るなら、このやり方で問題ないですが、少々つらいです。

どうするとよいか

基本的には、テンプレートで使うものをenumに実装するのがよいです。
また、テンプレートから直接呼び出すよりも、必要なインスタンスをControllerから渡す形に変えるとよいです。

enumの全要素を使う場合(セレクトボックスなど)

Javaのenumには、暗黙的なpublic static T[] values()というメソッドがあります。
全要素を列挙するには、これを利用するといいです。(定番といえば、定番ネタです。)


public enum UserStatus {
   ACTIVE(1, "有効"),
   INACTIVE(0, "無効"),
   PENDING(2, "保留"),
   FORBIDDEN(3, "強制停止")
   ;

   private final int value;

   private final String viewName;

   private UserStatus(int value, String viewName) {
       this.value = value;
       this.viewName = viewName;
   }

   public int getValue() {
       return this.value;
   }

   public String getViewName() {
       return this.viewName;
   }

}

@Controller
public class SampleController

@RequestGetMapping
public ModelAndView index() {
   ModelAndView model = new ModelAndView();
   model.setAttribute("statusList", UserStatus.values());
   return model;
}

<select name="status">
    <option th:each="status : statusList}" 
            th:value="${status.value}" th:text="${status.viewName}">
    </option>
</select>

フォーム入力されたものをenumとして受け取る

上のやり方では、enumとして値を受け取るには不便です。
enumには name()valueOf(String name) という、暗黙的に宣言されたメソッドがあります。

name()につけた名前が返却され、valueOf(String name) は、名前が一致したら対応するインスタンスを返すものです。Springがこれらを使って対応づけしてくれます。

<select name="status">
    <option th:each="status : statusList}" 
            th:value="${status.name()}" th:text="${status.viewName}">
    </option>
</select>
public class NewUserForm {

  private UserStatus status;

  // ... 

}
@RequestPutMapping
public ModelAndView create(NewUserForm form) {
   ModelAndView model = new ModelAndView();
   // ... 
   return model;
}

とすると、フォームで選択されたものを、enumにマッピングして受け取ることができます。

enumのまま渡しても、暗黙のうちに、name()が使われるので、下記のように書けます。

<option th:each="status : statusList}" 
            th:value="${status}" th:text="${status.viewName}">
</option>

特定のenumの判定

これはenumにisXXXというメソッドを足すやり方があります。


public enum UserStatus {
   // ...

   public boolean isActive() {
       return this == ACTIVE;
   }

   public boolean isForbidden() {
       return this == FORBIDDEN;
   }

}
<div th:if="${status.isForbidden()}">
    This user is forbidden.
</div>

このisXXXメソッドは、テンプレートだけでなく、ビジネスロジックを書く場面でも有効です。
たとえば、有効なユーザだけに絞り込むなら、こんな風になります。


public class User {

  //...

  private final UserStatus status;

  public UserStatus getStatus() {
     return this.status
  }

}

   List<User> users = ...
   List<User> activeUsers = users.stream()
                                 .filter(x -> x.getStatus().isActive())
                                 .collect(Collectors.toList());

まとめ

ぐぐって、ベストプラクティスが見つかることもあれば、そうでないこともあります。
また、検索して見つかった方法が自分の現状にあっているか、その判断基準も検索してみつかることもあまりないです。
テンプレートはいろんな関心事が入りやすいので、実装するときにはいろんな判断に迫られます。

参考リンク

16
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
20