3

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?

1 Answer 1

7

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

}
Sign up to request clarification or add additional context in comments.

4 Comments

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
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.
[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.
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!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.