dropdown lists with Thymeleaf and SpringBoot

14,847

You have forgot indicate the field from where it has to take the selected value:

Example: I supousse the User class has an attribute for institution.

<div class="form-group">
 <label class="col-md-3 control-label">Institution</label>
     <div class="col-md-5">
     <div th:if="${institutionList != null and not #lists.isEmpty(institutionList)}">
         <select th:field="*{institution}">
             <option th:each="dropDownItem : ${institutionList}"
                             th:value="${dropDownItem.name}"
                             th:text="${dropDownItem.name}" />
         </select>
    </div>
    <div th:if="${institutionList == null or lists.isEmpty(institutionList)}">
        <div>"No Institutions were found, please create some first"</div>
    </div>
</div>

More info: http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#dropdownlist-selectors

EDIT: You need to indicate to your app how convert a Id of Insitution returned inside form (String type) to a Institution entity. For that you have to use a Converter.

First change the value of option to institutionId:

<div class="form-group">
 <label class="col-md-3 control-label">Institution</label>
     <div class="col-md-5">
     <div th:if="${institutionList != null and not #lists.isEmpty(institutionList)}">
         <select th:field="*{institution}">
             <option th:each="dropDownItem : ${institutionList}"
                             th:value="${dropDownItem.institutionId}"
                             th:text="${dropDownItem.name}" />
         </select>
    </div>
    <div th:if="${institutionList == null or lists.isEmpty(institutionList)}">
        <div>"No Institutions were found, please create some first"</div>
    </div>
</div>

You have to create a class that implements the Converter interface.

@Component
public class StringToInstitution implements Converter<String, Institution> {

@Autowired
private InstitutionRepository repository; //Or the class that implments the repository.

    @Override
    public Institution convert(String arg0) {
        Long id = new Long(arg0);
        return repository.findOne(id);
    }

}
Share:
14,847
sonoerin
Author by

sonoerin

Updated on July 14, 2022

Comments

  • sonoerin
    sonoerin almost 2 years

    I have a Spring Boot 1.3 app that uses Thymeleaf on the HTML. I have page of users and when I edit them, I would like the user institution selected from a list. I have the window showing everything properly, but when I select the institution, the controller does not show the selected value.

    Here is my HTML:

     <div class="form-group">
         <label class="col-md-3 control-label">Institution</label>
             <div class="col-md-5">
             <div th:if="${institutionList != null and not #lists.isEmpty(institutionList)}">
                 <select>
                     <option th:each="dropDownItem : ${institutionList}"
                                     th:value="${dropDownItem.name}"
                                     th:text="${dropDownItem.name}" />
                 </select>
            </div>
            <div th:if="${institutionList == null or lists.isEmpty(institutionList)}">
                <div>"No Institutions were found, please create some first"</div>
            </div>
        </div>
     </div>
    

    And here is my controller

    @Controller
    @PreAuthorize("hasRole('Admin')")
    @RequestMapping("/admin")
    public class AdminController {
        @Transactional
        @PreAuthorize("hasRole('ADMIN')")
        @RequestMapping(value = "/addUser", method = RequestMethod.POST)
        public String checkPersonInfo(@ModelAttribute User user, Model model, Authentication authentication) {
            // user does not have the selected institution set
            customUserDetailsService.createNewUser(user);
            updateModelWithAllUsers(model);
            return "admin";
        }
    
        private void updateModelWithAllUsers(Model model) {
            model.addAttribute(USERLIST, customUserDetailsService.findAllUsers());
            model.addAttribute(INSTITUTION_LIST, institutionService.findAll());
            model.addAttribute("user", new User());
        }
    
    ...
    }
    

    Here is my User:

    @Entity
    @Table(name = "Users")
    public class User implements UserDetails {
     @Id
        private String username;
    
        @Column(nullable = false)
        private String password;
    
        private boolean enabled;
        private boolean accountNonExpired;
        private boolean accountNonLocked;
        private boolean credentialsNonExpired;
        private String companyName;
        private String email;
    
        @ElementCollection(fetch=FetchType.EAGER)
        @CollectionTable(name="Authorities", joinColumns=@JoinColumn(name="username"))
        @Column(name="authority")
        private Set<String> roles = new HashSet<String>();
    
        @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
        @JoinColumn(name = "institutionId", nullable = false)
        private Institution institution;
    ... getters & setters
    }
    

    And my Institution:

    @Entity
    @Table(name = "Institution")
    public class Institution {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long institutionId;
        private String name;
    
        @OneToMany(mappedBy = "institution", fetch = FetchType.EAGER)
        @Fetch(value = FetchMode.SUBSELECT)
        List<User> users = new ArrayList<User>();
        ... getters & setters
    }
    

    When the "/addUser" gets the User object from the UI, the institution is null.

    EDIT I used Sergios suggestion to use select th:field="${user.institution}" as well as select th:field="*{institution}"

    but that gives me an error:

    org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'user' on field 'institution': rejected value [com.security.Institution@3e3945d2]; codes [typeMismatch.user.institution,typeMismatch.institution,typeMismatch.com. .security.Institution,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.institution,institution]; arguments []; default message [institution]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.security.Institution' for property 'institution'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.lang.Long for value 'com.security.Institution@3e3945d2'; nested exception is java.lang.NumberFormatException: For input string: "com. .security.Institution@3e3945d2"]

    Not sure if I am reading this correctly, but does that mean Thymeleaf is trying to pass the Institution.name to the user.institution field?

    Can anyone offer any advice on how to do this?

  • sonoerin
    sonoerin about 8 years
    When I make that change, I get an error about type conversion 'org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.lang.Long for value 'com.security.Institution@27d1fda1'; nested exception is java.lang.NumberFormatException: For input string: "com.security.Institution@27d1fda1"' My User has a Institution attribute, and my drop down list is of that type - so not sure why its not setting the object
  • Sergio Garcia Alonso
    Sergio Garcia Alonso about 8 years
    When you send the form in the field institution you send the name of Insitution and in your User you have a attribute of Institution class then Spring try to recovery the entity from DB but it can't because Spring have a String and the id of Institution is Long.
  • Sergio Garcia Alonso
    Sergio Garcia Alonso about 8 years
    [Continuation] First you need return the Id value of institution: th:value="${dropDownItem.id}" (Or the name of id attribute) And in your app you need to have a Convert class from String to Institution, in this convert you can convert the String to Long and use your InstitutionRepository to obtain the Institution.
  • sonoerin
    sonoerin about 8 years
    Thank you so much, I had never heard of the Convert class. I guess I was expecting the Institution to come back as JSON or an object or something. That worked wonderfully, thank you for the explanation and time!