Spring 3 MVC:动态表单中的一对多(在创建/更新时添加/删除)

我正在寻找一种使用 的解决方案。我正在使用 , 和 。我在网上发现了许多曲目,但没有任何可用的完整示例。 ***

背景

我有三个JPA实体:

Consult.java (1)

@Entity

@Table(name = "consult")

public class Consult

private Integer id;

private String label;

private Set<ConsultTechno> consultTechnos;

/* getters & setters */

}

ConsultTechno.java (2)

@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)

@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中动态添加两个字段:

<input type="text" name="consult.consultTechnos[].techno.id" />

<input type="text" name="consult.consultTechnos[].level" />

当然,每次用户单击按钮时,都应重新添加这两个字段,以此类推。以我input type="text"为例,但最后,这些字段将是2 select

应该涵盖四种操作:

  1. 新的主实体时 子实体
  2. 新的主实体时 子实体
  3. 新的主实体时 子实体
  4. 新的主实体时 子实体

问题

布局部分 已经可以使用,但是在发布表单时,我无法将动态添加的字段绑定到我的@ModelAttribute consult

您对如何做这种工作有任何想法吗?我希望我已经足够清楚了…

提前致谢 :)

回答:

这一点在网络上仍然很混乱并且不清楚,因此这是我解决问题的方法。此解决方案可能不是最优化的解决方案,但在 主实体时可以使用。

理论

  1. 对于应该动态管理的一对多关系,请使用a List代替a Set

  2. 将初始化ListAutoPopulatingList。这是一个懒惰列表,允许动态 元素。

  3. 添加一个属性removeint,以你的子实体。这将充当布尔标志的一部分,并且在动态 元素时将很有用。

  4. 在发布形式,坚持只有具有该标志的元素remove0(即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>

希望能有所帮助 :)

以上是 Spring 3 MVC:动态表单中的一对多(在创建/更新时添加/删除) 的全部内容, 来源链接: utcz.com/qa/406004.html

回到顶部