thymeleafでenumを扱うためのtips

今年になって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()`というメソッドがあります。
全要素を列挙するには、これを利用するといいです。（定番といえば、定番ネタです。）

```Java

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;
   }

}
```

```java

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

```java

public class User {

  //...

  private final UserStatus status;

  public UserStatus getStatus() {
     return this.status
  }

}
```

```java

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

```


# まとめ

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

# 参考リンク

https://qiita.com/seijikohara/items/5901d3e4e7fdbd1d61f5

https://qiita.com/ksby/items/7cf5235f6061ef1600e2

https://qiita.com/suke_masa/items/bd242617a2b9bb773cbd

https://www.baeldung.com/thymeleaf-enums

https://gloryof.hatenablog.com/entry/20150705/1436074059

https://hang-hu.github.io/java-core/2017/03/12/Compare-enum-in-thymeleaf.html

https://frontbackend.com/thymeleaf/using-enums-in-thymeleaf

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Enum.html