Spring 3 MVC:动态表单中的一对多(创建/更新时添加/删除) [英] Spring 3 MVC: one-to-many within a dynamic form (add/remove on create/update)
问题描述
我正在寻找一种解决方案,使用 jQuery 管理HTML表单中的一对多关系。我正在使用 Spring , Spring MVC 和 Hibernate 进行开发。我在网络上发现了很多曲目,但没有任何完整的示例。
背景
有三个JPA实体:
$ b $ p code> Consult.java (1)
$ b
@Entity
@Table(name =consult)
public class Consult
私人整数ID;
私人字符串标签;
private Set< ConsultTechno> consultTechnos;
/ * getters& setter * /
}
ConsultTechno.java (2)
$ b
@实体
@Table(name =consult_techno)
public class ConsultTechno {
private Integer id;
私人Techno技术;
私人咨询咨询;
private String level;
/ * getters& setter * /
}
Techno.java (3)
@实体
@Table(name =techno)
public class Techno {
private Integer id;
私人字符串标签;
private Set< ConsultTechno> consultTechnos;
/ * getters& setters * /
}
如图所示,Consult(1)包含 n ConsultTechnos(2),其特征在于级别和Techno(3)。
使用HTML表单,我想要一个添加一个techno
按钮,它动态地在DOM:
< input type =textname =consult.consultTechnos []。techno.id/>
< input type =textname =consult.consultTechnos []。level/>
当然,每次用户点击按钮时,应该重新添加这两个字段,等等。我为这个例子选择了 input type =text
,但最后这些字段将是两个 select
。
应包括四种操作:
- 创建新的主实体 时添加子实体
- 创建 strong>新的主实体
- 在更新新的主实体时添加子实体
- 在更新新的主实体时移除子实体 问题
该布局部分已经可以工作,但是当发布表单时,我无法设法将动态添加的字段绑定到我的
@ModelAttribute consult
。
你有什么想法如何做这种工作?我希望我已经清楚了......
预先感谢
:)
解决方案这一点在网络上仍然相当混乱和不清楚,所以这里是我解决问题的方式。这个解决方案可能不是最优化的解决方案,但是它可以在创建和更新主实体时使用。
理论
-
使用
List
而不是Set
为您的一对多关系进行动态管理。
初始化您的 -
- Add a child entity when creating a new master entity
- Remove a child entity when creating a new master entity
- Add a child entity when updating a new master entity
- Remove a child entity when updating a new master entity
Use a
List
instead of aSet
for your one-to-many relations which should be dynamically managed.Initialize your
List
as anAutoPopulatingList
. It's a lazy list which allows to add dynamically elements.Add an attribute
remove
ofint
to your child entity. This will play the part of a boolean flag and will be usefull when removing dynamically an element.When posting the form, persist only the elements that have the flag
remove
on0
(i.e.false
).
列表
作为 AutoPopulatingList
。这是一个懒惰的列表,它允许动态添加元素。 添加一个属性 remove
int
添加到您的子实体。这将扮演布尔标志的一部分,当动态地移除一个元素时,它将是有用的。 当发布表单时,坚持只有在 0
(即 false $ c $>)上标记的元素移除
练习
工作满例如:雇主有很多雇员,雇员有一个雇主。实体: $ c> Employer.java
@Entity
@表(名称=雇主)
公共类雇主
私人整数ID;
私人字符串名字;
私人字符串姓氏;
私人字符串公司;
私人清单<员工>雇员; //一对多
/ * getters& setters * /
}
Employee.java
$ b
@Entity
@Table(name =员工)
公共类员工{
私人整数ID;
@Transient //表示不是数据库字段
私有整数删除; //布尔标志
private String firstname;
私人字符串姓氏;
私人雇主雇主; //多对一
/ * getters& setters * /
}
控制器:
EmployerController.java
@Controller
@RequestMapping(employer)
public class EmployerController {
//管理动态添加或删除的员工
私人列表<员工> ; manageEmployees(雇主雇主){
//存储不应持续的员工
List< Employee> employees2remove = new ArrayList< Employee>(); (Iterator< Employee> i = employer.getEmployees()。iterator(); i.hasNext();){
if(employer.getEmployees()!= null){
Employee employee = i.next();
//如果remove标志为true,则从列表中删除雇员
if(employee.getRemove()== 1){
employees2remove.add(employee);
i.remove();
//否则,执行链接
} else {
employee.setEmployer(employer);
}
}
}
return employees2remove;
}
// - 创建一个新雇主----------
@RequestMapping(value =create,method = RequestMethod.GET)
public String create(@ModelAttribute雇主雇主,模型模型){
//应该初始化AutoPopulatingList
return create(employer,model,true);
私有字符串创建(雇主雇主,模型模型,布尔初始化){
if(init){
//初始化AutoPopulatingList
雇主.setEmployees(new AutoPopulatingList< Employee>(Employee.class));
}
model.addAttribute(type,create);
返回雇主/编辑;
$ b @RequestMapping(value =create,method = RequestMethod.POST)
public String create(@Valid @ModelAttribute雇主雇主,BindingResult bindingResult,模型模型){
if(bindingResult.hasErrors()){
//不应该重新初始化AutoPopulatingList
return create(employer,model,false);
}
//调用私有方法
manageEmployees(employer);
//坚持雇主
employerService.save(雇主);
returnredirect:employer / show /+ employer.getId();
}
// - 更新现有雇主----------
@RequestMapping(value =update / {pk} ,method = RequestMethod.GET)
public String update(@PathVariable Integer pk,@ModelAttribute雇主雇主,模型模型){
//添加您自己的getEmployerById(pk)
model.addAttribute (类型,更新);
返回雇主/编辑;
}
@RequestMapping(value =update / {pk},method = RequestMethod.POST)
public String update(@PathVariable Integer pk,@Valid @ModelAttribute Employer雇主,BindingResult bindingResult,模型模型){
//添加您自己的getEmployerById(pk)
if(bindingResult.hasErrors()){
return update(pk,employer,model);
}
列表< Employee> employees2remove = manageEmployees(雇主);
//首先,保存雇主
employerService.update(雇主);
//然后,删除以前关联的员工,这些员工现在应该被删除
(Employee employee:employees2remove){
if(employee.getId()!= null){
employeeService.delete(雇员);
}
}
返回redirect:employer / show /+ employer.getId();
}
// - 显示现有雇主----------
@RequestMapping(value =show / {pk} ,method = RequestMethod.GET)
public String show(@PathVariable Integer pk,@ModelAttribute雇主雇主){
//添加您自己的getEmployerById(pk)
returnemployer / show ;
}
}
查看:
employer / edit.jsp
<%@ taglib prefix =curi =http://java.sun.com/jsp/jstl/core
%><%@ taglib前缀=formuri =http://www.springframework.org/tags/form
%><%@ taglib prefix =fnuri =http://java.sun。 com / jsp / jstl / functions
%><%@ page language =javacontentType =text / html; charset = UTF-8pageEncoding =UTF-8
% ><!DOCTYPE HTML>
< html>
< head>
< title>编辑< / 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(){
//以当前列表的大小开始索引
var index = $ {fn:length(employer.employees)}; $ b $(b)
//添加一个新的Employee
$(#add)。off(click)。on(click,function(){
$(this)。 before(function(){
var html ='< div id =employees'+ index +'.wrapperclass =hidden>';
html + ='< input type =textid =employees'+ index +'.firstnamename =employees ['+ index +'] .firstname/>';
html + ='< input type =text id =employees'+ index +'.lastnamename =employees ['+ index +'] .lastname/>';
html + ='< input type =hiddenid = 'employees'+ index +'.removename =employees ['+ index +'] .removevalue =0/>';
html + ='< a href =# class =employees.removedata-index ='+ index +'>删除< ; / a>';
html + =< / div>;
返回html;
});
$(#employees+ index +\\.wrapper)。show();
index ++;
返回false;
}); $(b
$ b)//删除一个员工
$(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:otherwise>< c:set var =actionUrlvalue =employer / update / $ {employer.id}/>< / c:otherwise>
< form:hidden path =id/>
< table>
< tr>
< td>< form:label path =firstname>名字< / form:label>< / td>
< td>< form:input path =firstname/>< form:errors path =firstname/>< / td>
< / tr>
< tr>
< td>< form:label path =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>员工< / td>
< td>
< c:forEach items =$ {employer.employees}varStatus =loop>
<! - 添加包装div - >
< c:when test =$ {employer.employees [loop.index] .remove eq 1}>
< div id =employees $ {loop.index} .wrapperclass =hidden>
< / c:当>
< c:其他>
< div id =employees $ {loop.index} .wrapper>
< / c:否则>
<! - 生成字段 - >
< form:input path =employees [$ {loop.index}]。firstname/>
< form:input path =employees [$ {loop.index}]。lastname/>
<! - 添加删除标志 - >
< c:when test =$ {employees [loop.index] .remove eq 1}>< c:set var =hiddenValuevalue =1/>< / c :当>
< c:否则>< c:set var =hiddenValuevalue =0/>< / c:否则>
< form:hidden path =employees [$ {loop.index}]。removevalue =$ {hiddenValue}/>
<! - 添加链接以删除员工 - >
< a href =#class =employees.removedata-index =$ {loop.index}>删除< / a>
< / div>
< / c:forEach>
< button id =addtype =button> add< / button>
< / td>
< / tr>
< / table>
< button type =submit>确定< / button>
< / form:form>
< / body>
< / html>
希望可以帮助:)
I'm looking for a solution to manage a one-to-many relation within an HTML form using jQuery. I'm developing with Spring, Spring MVC and Hibernate. I found many tracks on the web, but not any working full-example.
The background
I've three JPA entities:
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 */
}
As shown, a Consult (1) contains n ConsultTechnos (2), which are caracterized by a level and a Techno (3).
The needs
Using an HTML form, I would like to have a Add a techno
button which dynamically adds two fields in the DOM:
<input type="text" name="consult.consultTechnos[].techno.id" />
<input type="text" name="consult.consultTechnos[].level" />
Of course, each time the user clicks on the button, those two fields should be re-added, etc. I chose input type="text"
for the example, but at the end, the fields will be two select
.
Four kinds of operation should be covered:
The problem
That layout part already works, but when posting the form, I can't manage to bind the dynamically added fields to my @ModelAttribute consult
.
Do you have any idea of how to do that kind of jobs? I hope I've been clear enough...
Thanks in advance :)
This point is still quite confusing and unclear on the web, so here is the way I solved my problem. This solution is probably not the most optimized one, but it works when creating and updating a master entity.
Theory
Practice
A working full-example: an employer has many employees, an employee has one employer.
Entities:
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 */
}
Controller:
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";
}
}
View:
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>
Hope that could help :)
这篇关于Spring 3 MVC:动态表单中的一对多(创建/更新时添加/删除)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!