Issue
My domain model consist of an Employee
and Certificate
. One Employee can reference/have many certificates (one-to-many relationship). The full list of certificates could be get from the certificateService
.
To assign some special certificate to the employee I used th:checkbox
element from thymeleaf as follow:
<form action="#" th:action="@{/employee/add}" th:object="${employee}" method="post">
<table>
<tr>
<td>Name</td>
<td><input type="text" th:field="*{name}"></td>
</tr>
<tr>
<td>Certificate</td>
<td>
<th:block th:each="certificate , stat : ${certificates}">
<input type="checkbox" th:field="*{certificates}" name="certificates" th:value="${certificate.id]}"/>
<label th:text="${certificate.name}" ></label>
</th:block>
</td>
</tr>
<tr>
<td colspan="2"><input type="submit" value="Add"/></td>
</tr>
</table>
</form>
Now when I'm trying to submit the HTML form I always get following error:
400 - The request sent by the client was syntactically incorrect.
My question is: How to correctly bind checkbox elements to the object list with thymeleaf?
Controller
@RequestMapping(value = "/add" , method = RequestMethod.GET)
public String add(Model model) {
model.addAttribute("employee",new Employee());
model.addAttribute("certificates",certificateService.getList());
return "add";
}
@RequestMapping(value = "/add" , method = RequestMethod.POST)
public String addSave(@ModelAttribute("employee")Employee employee) {
System.out.println(employee);
return "list";
}
Employee Entity
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private int id;
@Column(name = "Name")
private String name;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "emp_cert",
joinColumns = {@JoinColumn(name = "employee_id")},
inverseJoinColumns = {@JoinColumn(name = "certificate_id")})
private List<Certificate> certificates;
public Employee() {
if (certificates == null)
certificates = new ArrayList<>();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Certificate> getCertificates() {
return certificates;
}
public void setCertificates(List<Certificate> certificates) {
this.certificates = certificates;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "certificates size = " + certificates.size() + " ]";
}
}
Certificate Entity
@Entity
@Table(name = "certificate")
public class Certificate {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int id;
@Column(name = "name")
private String name;
@ManyToMany(mappedBy = "certificates")
private List<Employee> employees;
public Certificate() {
if (employees == null)
employees = new ArrayList<>();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Certificate other = (Certificate) obj;
if (id != other.id)
return false;
return true;
}
}
Solution
I used a custom solution to solve this issue, i implemented it by sending an array of certificates id to controller and received it as requestParam . The require changes are defined below .
View
<tr>
<td>Certificate</td>
<td>
<th:block th:each="certificate : ${certificates}">
<input type="checkbox" name="cers" th:value="${certificate.id}"/>
<label th:text="${certificate.name}"></label>
</th:block>
</td>
</tr>
Controller
@RequestMapping(value = "/add" , method = RequestMethod.POST)
public String addSave(
@ModelAttribute("employee")Employee employee ,
@RequestParam(value = "cers" , required = false) int[] cers ,
BindingResult bindingResult , Model model) {
if(cers != null) {
Certificate certificate = null ;
for (int i = 0; i < cers.length; i++) {
if(certificateService.isFound(cers[i])) {
certificate = new Certificate();
certificate.setId(cers[i]);
employee.getCertificates().add(certificate);
}
}
for (int i = 0; i < employee.getCertificates().size(); i++) {
System.out.println(employee.getCertificates().get(i));
}
}
Answered By - Al-Mustafa Azhari
Answer Checked By - Timothy Miller (JavaFixing Admin)