我正在寻找一种使用 jQuery* 在HTML表单中管理一对多关系 的解决方案。我正在使用 Spring , Spring MVC 和 Hibernate进行开发 。我在网上发现了许多曲目,但没有任何可用的完整示例。 ***
我有三个JPA实体:
Consult.java (1)
Consult.java
@Entity @Table(name = "consult") public class Consult private Integer id; private String label; private Set<ConsultTechno> consultTechnos; /* getters & setters */ }
ConsultTechno.java (2)
ConsultTechno.java
@Entity @Table(name = "consult_techno") public class ConsultTechno { private Integer id; private Techno techno; private Consult consult; private String level; /* getters & setters */ }
Techno.java (3)
Techno.java
@Entity @Table(name="techno") public class Techno { private Integer id; private String label; private Set<ConsultTechno> consultTechnos; /* getters & setters */ }
如图所示,Consult(1)包含 n个 ConsultTechnos(2),它们由一个 级别 和一个Techno(3)来表征。
使用HTML表单,我希望有一个Add a techno按钮,可以在DOM中动态添加两个字段:
Add a techno
<input type="text" name="consult.consultTechnos[].techno.id" /> <input type="text" name="consult.consultTechnos[].level" />
当然,每次用户单击按钮时,都应重新添加这两个字段,以此类推。以我input type="text"为例,但最后,这些字段将是2 select。
input type="text"
select
应该涵盖四种操作:
该 布局部分 已经可以使用,但是在发布表单时,我无法将动态添加的字段绑定到我的@ModelAttribute consult。
@ModelAttribute consult
您对如何做这种工作有任何想法吗?我希望我已经足够清楚了…
提前致谢 :)
:)
这一点在网络上仍然很混乱并且不清楚,因此这是我解决问题的方法。此解决方案可能不是最优化的解决方案,但在 创建和更新 主实体时可以使用。
对于应该动态管理的一对多关系,请使用a List代替a Set。
List
Set
将初始化List为AutoPopulatingList。这是一个懒惰列表,允许动态 添加 元素。
AutoPopulatingList
添加一个属性remove的int,以你的子实体。这将充当布尔标志的一部分,并且在动态 删除 元素时将很有用。
remove
int
在发布形式,坚持只有具有该标志的元素remove上0(即false)。
0
false
一个完整的例子:一个雇主有很多雇员,一个雇员有一个雇主。
Employer.java
@Entity @Table(name = "employer") public class Employer private Integer id; private String firstname; private String lastname; private String company; private List<Employee> employees; // one-to-many /* getters & setters */ }
Employee.java
@Entity @Table(name = "employee") public class Employee { private Integer id; @Transient // means "not a DB field" private Integer remove; // boolean flag private String firstname; private String lastname; private Employer employer; // many-to-one /* getters & setters */ }
EmployerController.java
@Controller @RequestMapping("employer") public class EmployerController { // Manage dynamically added or removed employees private List<Employee> manageEmployees(Employer employer) { // Store the employees which shouldn't be persisted List<Employee> employees2remove = new ArrayList<Employee>(); if (employer.getEmployees() != null) { for (Iterator<Employee> i = employer.getEmployees().iterator(); i.hasNext();) { Employee employee = i.next(); // If the remove flag is true, remove the employee from the list if (employee.getRemove() == 1) { employees2remove.add(employee); i.remove(); // Otherwise, perform the links } else { employee.setEmployer(employer); } } } return employees2remove; } // -- Creating a new employer ---------- @RequestMapping(value = "create", method = RequestMethod.GET) public String create(@ModelAttribute Employer employer, Model model) { // Should init the AutoPopulatingList return create(employer, model, true); } private String create(Employer employer, Model model, boolean init) { if (init) { // Init the AutoPopulatingList employer.setEmployees(new AutoPopulatingList<Employee>(Employee.class)); } model.addAttribute("type", "create"); return "employer/edit"; } @RequestMapping(value = "create", method = RequestMethod.POST) public String create(@Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { // Should not re-init the AutoPopulatingList return create(employer, model, false); } // Call the private method manageEmployees(employer); // Persist the employer employerService.save(employer); return "redirect:employer/show/" + employer.getId(); } // -- Updating an existing employer ---------- @RequestMapping(value = "update/{pk}", method = RequestMethod.GET) public String update(@PathVariable Integer pk, @ModelAttribute Employer employer, Model model) { // Add your own getEmployerById(pk) model.addAttribute("type", "update"); return "employer/edit"; } @RequestMapping(value = "update/{pk}", method = RequestMethod.POST) public String update(@PathVariable Integer pk, @Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) { // Add your own getEmployerById(pk) if (bindingResult.hasErrors()) { return update(pk, employer, model); } List<Employee> employees2remove = manageEmployees(employer); // First, save the employer employerService.update(employer); // Then, delete the previously linked employees which should be now removed for (Employee employee : employees2remove) { if (employee.getId() != null) { employeeService.delete(employee); } } return "redirect:employer/show/" + employer.getId(); } // -- Show an existing employer ---------- @RequestMapping(value = "show/{pk}", method = RequestMethod.GET) public String show(@PathVariable Integer pk, @ModelAttribute Employer employer) { // Add your own getEmployerById(pk) return "employer/show"; } }
employer/edit.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %><%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><!DOCTYPE HTML> <html> <head> <title>Edit</title> <style type="text/css">.hidden {display: none;}</style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script> <script type="text/javascript"> $(function() { // Start indexing at the size of the current list var index = ${fn:length(employer.employees)}; // Add a new Employee $("#add").off("click").on("click", function() { $(this).before(function() { var html = '<div id="employees' + index + '.wrapper" class="hidden">'; html += '<input type="text" id="employees' + index + '.firstname" name="employees[' + index + '].firstname" />'; html += '<input type="text" id="employees' + index + '.lastname" name="employees[' + index + '].lastname" />'; html += '<input type="hidden" id="employees' + index + '.remove" name="employees[' + index + '].remove" value="0" />'; html += '<a href="#" class="employees.remove" data-index="' + index + '">remove</a>'; html += "</div>"; return html; }); $("#employees" + index + "\\.wrapper").show(); index++; return false; }); // Remove an Employee $("a.employees\\.remove").off("click").on("click", function() { var index2remove = $(this).data("index"); $("#employees" + index2remove + "\\.wrapper").hide(); $("#employees" + index2remove + "\\.remove").val("1"); return false; }); }); </script> </head> <body> <c:choose> <c:when test="${type eq 'create'}"><c:set var="actionUrl" value="employer/create" /></c:when> <c:otherwise><c:set var="actionUrl" value="employer/update/${employer.id}" /></c:otherwise> </c:choose> <form:form action="${actionUrl}" modelAttribute="employer" method="POST" name="employer"> <form:hidden path="id" /> <table> <tr> <td><form:label path="firstname">Firstname</form:label></td> <td><form:input path="firstname" /><form:errors path="firstname" /></td> </tr> <tr> <td><form:label path="lastname">Lastname</form:label></td> <td><form:input path="lastname" /><form:errors path="lastname" /></td> </tr> <tr> <td><form:label path="company">company</form:label></td> <td><form:input path="company" /><form:errors path="company" /></td> </tr> <tr> <td>Employees</td> <td> <c:forEach items="${employer.employees}" varStatus="loop"> <!-- Add a wrapping div --> <c:choose> <c:when test="${employer.employees[loop.index].remove eq 1}"> <div id="employees${loop.index}.wrapper" class="hidden"> </c:when> <c:otherwise> <div id="employees${loop.index}.wrapper"> </c:otherwise> </c:choose> <!-- Generate the fields --> <form:input path="employees[${loop.index}].firstname" /> <form:input path="employees[${loop.index}].lastname" /> <!-- Add the remove flag --> <c:choose> <c:when test="${employees[loop.index].remove eq 1}"><c:set var="hiddenValue" value="1" /></c:when> <c:otherwise><c:set var="hiddenValue" value="0" /></c:otherwise> </c:choose> <form:hidden path="employees[${loop.index}].remove" value="${hiddenValue}" /> <!-- Add a link to remove the Employee --> <a href="#" class="employees.remove" data-index="${loop.index}">remove</a> </div> </c:forEach> <button id="add" type="button">add</button> </td> </tr> </table> <button type="submit">OK</button> </form:form> </body> </html>
希望能有所帮助 :)