[JAVA] Tips for handling enums in thymeleaf

This year, I was involved in a project that uses Spring boot, and I tried to organize the knowledge I got about thymeleaf and enum.

Things that often come up when you google with "Thymeleaf enum"

With Spring or Spring boot, it's customary to use Thymeleaf in your template engine. It is common to use enums to manage the values and display names of select boxes on the input form.

This is an excerpt from https://www.baeldung.com/thymeleaf-enums, but when you google, you can see the following examples.

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

There is also such an example when judging whether it is a specific enum by judging the initial value.

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

The difficulty with this method

I often see it, but when I say that this is the best, I can't say anything.

  1. The FQDN part of the type is difficult to read (there is no amount of information for a long time)
  2. If you try to review the package structure, you have to modify the template
  3. Even if you rename the enum, you have to modify the template.

The point is that some things that have nothing to do with the template come in, which has a negative impact on readability and maintainability. If you want to make something that works for the time being, this method is fine, but it's a little painful.

What to do

Basically, it's better to implement what you use in the template in the enum. Also, rather than calling directly from the template, it is better to change the form to pass the required instance from the Controller.

When using all elements of enum (select box etc.)

Java enums have an implicit method called public static T [] values (). Use this to enumerate all the elements. (Speaking of classics, it's a classic story.)


public enum UserStatus {
   ACTIVE(1, "Effectiveness"),
   INACTIVE(0, "Invalid"),
   PENDING(2, "on hold"),
   FORBIDDEN(3, "Forced stop")
   ;

   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>

Receive the form input as an enum

The above method is inconvenient to receive a value as an enum. The enum has implicitly declared methods, name () and valueOf (String name).

The name given to name () is returned, and valueOf (String name) returns the corresponding instance if the names match. Spring will use these for you.

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

Then you can map the selection in the form to the enum and receive it.

Judgment of a specific enum

There is a way to add a method called isXXX to the enum.


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>

This isXXX method is useful not only for templates but also for writing business logic. For example, if you want to narrow down to only valid users, it looks like this.


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());

Summary

Going around, you may or may not find best practices. Also, it is rare to find out whether the method found by searching is suitable for your current situation or not. Templates are easy to get into a lot of interests, so you have to make a lot of decisions when implementing them.

Reference link

Recommended Posts

Tips for handling enums in thymeleaf
Tips for handling pseudo-elements in Selenium
tips for java.nio.file.Path
Make a snippet for Thymeleaf in VS Code
Tips for generating files for eclipse projects in Gradle
Tips for setting the initial delay for Poller in XML
Tips for making good use of Canvas in Xcode
Error handling in Graphql-ruby
Tips for using Salesforce SOAP and Bulk API in Java
Refer to enum in Thymeleaf
[Ruby] Exception handling in functions
Gradle TIPS collection (for myself)
[Java] Tips for writing source
Exception handling techniques in Java
Format Timestamp type in Thymeleaf
Tips for making APIs generic
Leverage Either for individual exception handling in the Java Stream API