Spring MVC 和表单绑定:如何从列表中删除项目?
Posted
技术标签:
【中文标题】Spring MVC 和表单绑定:如何从列表中删除项目?【英文标题】:Spring MVC and form binding : how to remove an item from a List? 【发布时间】:2012-10-03 07:33:48 【问题描述】:我有一个Person
模型属性,其中包含一个列表 电子邮件。
我创建了一些 javascript 代码,用于从 html 电子邮件列表中删除元素。这是纯 JavaScript 客户端代码,没有 AJAX 调用。
提交后,我不明白为什么我会在相应的@Controller
方法中收到所有邮件,甚至是那些在HTML 中被删除的邮件。
谁能解释一下?
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" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$(document).ready(function()
$('.trash').click(function()
$(this.parentNode).remove();
);
);
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol id="emails">
<c:forEach items="$person.emails" varStatus="status">
<li><form:hidden path="emails[$status.index].order" class="emailsDisplayOrder"></form:hidden><form:input path="emails[$status.index].label"></form:input><form:input type="email" path="emails[$status.index].value"></form:input><input type="image" src="/resume/images/trash.png" class="trash" value="$status.index"></input></li>
</c:forEach>
</ol>
</td>
</tr>
</table>
</form:form>
</body>
</html>
控制器
@Controller
@SessionAttributes(types=Person.class, value="person")
public class PersonController
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
@Autowired()
public PersonController(ResumeManager resume)
this.resumeManager = resume;
@InitBinder
public void initBinder(WebDataBinder dataBinder)
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
@RequestMapping(value="/person/edit/save")
public String save(@ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status)
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails()); //this collection still contains client-side dropped objects
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
【问题讨论】:
你的意思是你删除了 和 元素?也许有些代码会更清晰 嗨杰罗姆,我的意思是所有子树的“li”节点(所以,是的,包含的“输入”元素也被删除了)。我刚刚编辑添加了 JSP 代码,这是您所期望的吗? 是的,谢谢,你的问题现在更清楚了 【参考方案1】:说明
当您加载带有<form:form modelAttribute="person" ...>
的页面时,有两种情况:
person
不存在,则创建一个空的Person
案例 2:如果 person
已经存在,则使用它
在所有情况下,当页面加载时,存在一个现有的person
。
当您提交表单时,Spring MVC 更新这个现有的person
仅包含提交的信息。
所以在情况 1 中,如果您提交电子邮件 1、2、3 和 4,Spring MVC 会在空的person
中添加 4 封电子邮件。在这种情况下对你来说没问题。
但是在情况 2 中(例如,当您在会话中编辑现有的 person
时),如果您提交电子邮件 1 和 2,但人已经有 4 封电子邮件,那么 Spring MVC 将仅替换电子邮件 1 和 2。电子邮件 3和 4 仍然存在。
一个可能的解决方案
可能不是最好的,但应该可以。
将remove
布尔值添加到Email
类:
...
public class Email
...
private boolean remove; // Set this flag to true to indicate that
// you want to remove the person.
...
在控制器的save
方法中,删除将remove
设置为true 的电子邮件。
最后,在你的 JSP 中,添加这个隐藏字段:
<form:hidden path="emails[$status.index].remove" />
并告诉您的 Javascript 在用户单击删除电子邮件时将输入值设置为 true。
【讨论】:
这回答了我的问题,但又引发了另一个问题:我应该如何删除列表条目? @JeromeDalbert 我正在尝试实施您建议的技巧。但是,在删除隐藏输入并发布表单之前将其值更改为 false 不起作用。我的意思是提交后值不会改变..你对此有什么想法吗?【参考方案2】:杰罗姆·达尔伯特一号的替代解决方案
杰罗姆的解决方案应该可以工作(再次感谢明确的答案和解决方案),但我不想修改商业模式。 所以这是我发现的方法:使用 java-script 标记要删除的 HTML 元素,并在表单提交时使用 ajax 调用实际删除它(我最初避免使用 ajax 来保持模型不变,直到用户提交,但这个解决方案保留了该要求)。
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" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></meta>
<link rel="stylesheet" href="<c:url value="/styles/resume.css"/>" type="text/css"></link>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"></link>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
<script src="/resume/js/jquery.editable-1.0.1.js"></script>
<title>Resumes manager</title>
<script>
$('.sortable').sortable(
update: function(event,ui)
var liElements = this.getElementsByTagName('li');
$(liElements).each(function(i, liElement)
var orderElements = liElement.getElementsByClassName('order');
$(orderElements).each(function (j, orderElement)
orderElement.value = i;
);
);
);
$('.trashable').click(function()
$(this.parentNode.childNodes).each(function(index, element)
if(element.src.match(/trash.png/) != null)
element.src = '/resume/images/back.png';
this.parentNode.className = 'trashed';
else if(element.src.match(/back.png/) != null)
element.src = '/resume/images/trash.png';
this.parentNode.className = '';
else
element.disabled = !element.disabled;
);
);
function trash(element)
var sfx = element.alt;
var lnk = ('/resume/person/edit/').concat(sfx);
$.ajax(
url: lnk
);
$('#personForm').submit(function()
var trashed = $(this).find('.trashed');
$(trashed).each(function(index, element)
var img = $(element).find('.trashable');
var tmp = $(img)[0];
trash(tmp);
);
);
);
</script>
</head>
<body>
<h1>Personal data</h1>
<form:form modelAttribute="person" action="/resume/person/edit/save" id="personForm" method="post" >
<table>
<tr>
<td>Email addresses:</td>
<td colspan="4">
<ol class="sortable">
<c:forEach items="$person.emails" varStatus="status">
<li><form:hidden path="emails[$status.index].order" class="order"></form:hidden><form:input path="emails[$status.index].label"></form:input><form:input type="email" path="emails[$status.index].value"></form:input><img src="/resume/images/trash.png" class="trashable" ></img></li>
</c:forEach>
</ol>
</td>
</tr>
<tr><td colspan="5"><form:button name="save" value="$person.id">$person.id == 0 ? 'save' : 'update'</form:button></td></tr>
</table>
</form:form>
</body>
</html>
控制器:
@Controller
@SessionAttributes(types=Person.class, value="person")
public class PersonController
private final static String PERSON_VIEW_NAME = "person-form";
private ResumeManager resumeManager;
@Autowired()
public PersonController(ResumeManager resume)
this.resumeManager = resume;
@InitBinder
public void initBinder(WebDataBinder dataBinder)
dataBinder.setDisallowedFields("id");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
@RequestMapping(value="/person/edit/id")
public String edit(@PathVariable("id") long personId, Model model)
Person p = this.resumeManager.getPersonById(personId);
if(p != null)
model.addAttribute("person", p);
return PERSON_VIEW_NAME;
else
return "redirect:/";
@RequestMapping(value="/person/edit/save")
public String save(@ModelAttribute(value="person") Person p, BindingResult result, SessionStatus status)
new PersonValidator().validate(p, result);
Collections.sort(p.getEmails());
this.resumeManager.savePerson(p);
return PERSON_VIEW_NAME;
@RequestMapping(value="/person/edit/dropEmail/id")
@ResponseBody
public void dropEmail(@ModelAttribute(value="person") Person p, @PathVariable("id") long id)
int i = 0;
for(Email e : p.getEmails())
if(e.getId() == id)
p.getEmails().remove(i);
break;
i++;
【讨论】:
以上是关于Spring MVC 和表单绑定:如何从列表中删除项目?的主要内容,如果未能解决你的问题,请参考以下文章