I have an edit form for a Project object. Each project has a list of roles associated with them out of the complete list of all available roles. I need a checkbox list to select the roles. I implemented it like in this code sample I found thanks to StackOverflow, using a Formatter: https://github.com/jmiguelsamper/thymeleafexamples-selectmultiple
The issue: The form I created allows me to select the roles and save them successfully. But when I display an existing project object to edit it, the roles already associated with that project are not checked in the list. All the checkboxes are clear.
The code sample above was with a String id. I use a Long id. I think that's the reason for the issue, but I don't know how to solve it. Should I drop the Formatter approach entirely? Is there a way to make this work?
This is my code so far:
Project class:
@Entity
public class Project
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
private List<Role> rolesNeeded;
public Project()
{
rolesNeeded = new ArrayList<>();
}
//getters and setters omitted for brevity
}
Role class:
@Entity
public class Role
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Column
private String name;
public Role() {}
//getters and setters omitted for brevity
}
Controller:
@Controller
public class ProjectController
{
@Autowired
private ProjectService projectService;
@Autowired
private RoleService roleService;
@RequestMapping(value = "/projects/save", method = RequestMethod.POST)
public String addProject(@Valid Project project)
{
projectService.save(project);
return "redirect:/";
}
@RequestMapping("/projects/{id}/edit")
public String editForm(@PathVariable Long id, Model model)
{
Project project = projectService.findById(id);
model.addAttribute("project", project);
model.addAttribute("allRoles", roleService.findAll());
return "project/form";
}
}
The RoleFormatter:
@Component
public class RoleFormatter implements Formatter<Role>
{
@Override
public Role parse(String id, Locale locale) throws ParseException
{
Role role = new Role();
role.setId(Long.parseLong(id));
return role;
}
@Override
public String print(Role role, Locale locale)
{
String id = role.getId() + "";
return id;
}
}
And finally the Thymeleaf form:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<section>
<div class="container wrapper">
<form th:action="@{/projects/save}" method="post" th:object="${project}">
<input type="hidden" th:field="*{id}"/>
<div>
<label for="project_name"> Project Name:</label>
<input type="text" id="project_name" th:field="*{name}"/>
</div>
<div>
<label>Project Roles:</label>
<ul class="checkbox-list">
<li th:each="role : ${allRoles}">
<input type="checkbox" th:id="${{role}}" th:value="${{role}}" th:field="*{rolesNeeded}" />
<span class="primary" th:text="${role.name}"></span>
</li>
</ul>
</div>
<div class="actions">
<button type="submit" value="Save" class="button">Save</button>
<a th:href="@{/}" class="button button-secondary">Cancel</a>
</div>
</form>
</div>
</section>
</body>
</html>
UPDATE
As discussed in the comments: when I do not use the Formatter like above, I get a 400 Bad Request error. This is the header data of the POST request. In this case I tried selecting two roles (id 1 and 3 as you can see below)
Request URL:http://localhost:8080/projects/save
Request Method:POST
Status Code:400 Bad Request
Remote Address:[::1]:8080
Referrer Policy:no-referrer-when-downgrade
Response Headers
Connection:close
Content-Language:en-GB
Content-Length:350
Content-Type:text/html;charset=ISO-8859-1
Date:Tue, 31 Oct 2017 20:10:09 GMT
Server:Apache-Coyote/1.1
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding:gzip, deflate, br Accept-Language:en-GB,en;q=0.9,en-US;q=0.8,fr;q=0.7
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:161
Content-Type:application/x-www-form-urlencoded
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/projects/add
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/62.0.3202.75
Safari/537.36
Form Data
id:
name:Implement recipe site
description:description
status:RUNNING
rolesNeeded:1
_rolesNeeded:on
_rolesNeeded:on
rolesNeeded:3
_rolesNeeded:on
_rolesNeeded:on