Spring – Select Tag with Object – Thymeleaf and Spring MVC

springspring-mvcthymeleaf

I'm trying to change the code of this example thymeleafexamples-stsm, so I changed enum type for class type:

Type.java

public class Type { 

    private Integer id; 
    private String type; 
   ...getters and setters 
}

SeedStarterMngController.java

@ModelAttribute("allTypes") 
    public List<Type> populateTypes() { 
        Type type1 = new Type(); 
        type1.setId(1); 
        type1.setType("OUTDOOR"); 

        Type type2 = new Type(); 
        type2.setId(2); 
        type2.setType("INDOOR"); 

        List<Type> tipos = new ArrayList<Type>(); 
        tipos.add(type1); 
        tipos.add(type2); 
        return tipos; 
    } 

seedstartermng.html

<select th:field="*{type}">
    <option th:each="type : ${allTypes}" th:value="${type}" th:text="${type.type}">Wireframe</option>
</select>

So, I can't Add Seed Starter.

My Output html is

<select id="type" name="type">
    <option value="thymeleafexamples.stsm.business.entities.Type@2c08cec0">OUTDOOR</option>
    <option value="thymeleafexamples.stsm.business.entities.Type@26cf024">INDOOR</option>
</select>

and the error is

Failed to convert property value of type java.lang.String to required
type thymeleafexamples.stsm.business.entities.Type for property type;
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String] to required type
[thymeleafexamples.stsm.business.entities.Type] for property type: no
matching editors or conversion strategy found

How I can do to be mapped to type correctly? I hope you can help me. Thank you.

Best Answer

I know this question is old but below answer may help someone as I could not find it easily.

To solve this issue, Thymeleaf uses Formatters to convert between Object and String.

  • In display phase (GET), Formatter Service will convert Object to String.
  • In submitting phase (POST), Formatter Service will convert String back to

    object.

First implement Formatter service for your Class to be used in tag:

@Service
public class TypeFormatter implements Formatter<Type> {

    @Autowired
    TypeService typeService;//Service -> DB

    @Override
    public String print(Type object, Locale locale) {
        return (object != null ? object.getId().toString() : "");
    }

    @Override
    public Type parse(String text, Locale locale) throws ParseException {
        Integer id = Integer.valueOf(text);
        return this.typeService.get(id);//return Type object form DB
    }
}

It is very simple class with two methods:

  • print: converts object to string.
  • parse: converts string to object.

Now, we've to tell Spring-Thymeleaf abbout our formatter or we may call it converter. To do that we've to register this formatter in our WebConfig (Configuration class whic extends WebMvcConfigurerAdapter):

@Configuration
@EnableWebMvc
@ComponentScan(value = { "your package" })
public class WebConfig extends WebMvcConfigurerAdapter {

....
    //Formatters

    @Autowired //Without autowire, this solution may not work
    private TypeFormatter typeFormatter;

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(typeFormatter);
    }
}

Now our solution is ready to be implemented in html file but how to tell Thymeleaf to apply conversion? Answer is by using th:field="*{type}" attribute and using Double-bracket syntax th:value="${{type}}":

<select th:field="*{type}">
    <option th:value="NULL" th:text="---Select Type---"></option>
    <option th:each="type : ${allTypes}" th:value="${{type}}" th:text="${type.type}">Wireframe</option>
</select>
  • th:field="*{type}" is applying registered Formatter service by default. It will convert type to String (here the string will be Type Id)
  • th:value="${{type}}" is converting type to string as well.
  • In submit, Spring will use the Formatter service to convert Id back to object.

Last thing to tell, sometimes we want to add a header to the dropdown list like "-----Select Type-----" in order to prevent default selection as well as explain to user. In this case you must set th:value="NULL" unless you'll get conversion error.

Related Topic