javascript Spring MVC 和表单绑定:如何从列表中删除项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12859847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring MVC and form binding : how to remove an item from a List?
提问by ch4mp
I have a Person
model attribute that contains a listof emails.
我有一个Person
包含电子邮件列表的模型属性。
I've created some JavaScript code that deletes elements from an HTML list of emails. This is pure JavaScript client side code, no AJAX call.
我创建了一些从 HTML 电子邮件列表中删除元素的 JavaScript 代码。这是纯 JavaScript 客户端代码,没有 AJAX 调用。
After submitting, I don't understand why I get allthe emails in the corresponding @Controller
method, even the ones that were deleted in the HTML.
提交后,不明白为什么会收到对应方法中的所有邮件@Controller
,甚至是在HTML中删除的邮件。
Can anyone please explain?
谁能解释一下?
JSP
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
控制器
@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;
}
}
采纳答案by Jerome Dalbert
Explanation
解释
When you loada page with <form:form modelAttribute="person" ...>
, there are two cases :
当您使用加载页面时<form:form modelAttribute="person" ...>
,有两种情况:
- case 1 : if
person
doesn't exist, it creates an emptyPerson
- case 2 : if
person
already exists, it uses it
- 情况 1:如果
person
不存在,则创建一个空的Person
- 情况2:如果
person
已经存在,则使用它
In all cases, when a page is loaded, there is an existing person
.
When you submit a form, Spring MVC updatesthis existing person
onlywith the submitted information.
在所有情况下,加载页面时,都会有一个现有的person
.
当您提交表单时,Spring MVC仅使用提交的信息更新此现有表单。person
So in case 1, if you submit email 1, 2, 3 and 4, Spring MVC will add 4 emails to the empty person
. No problem for you in this case.
因此,在情况 1 中,如果您提交电子邮件 1、2、3 和 4,Spring MVC 将添加 4 个电子邮件到空的person
. 在这种情况下,您没有问题。
But in case 2 (for example when you edit an existing person
in session), if you submit email 1 and 2, but person has already 4 emails, then Spring MVC will just replace email 1 and 2. Email 3 and 4 still exist.
但是在情况 2(例如,当您person
在会话中编辑现有的时),如果您提交电子邮件 1 和 2,但人已经有 4 个电子邮件,那么 Spring MVC 将只替换电子邮件 1 和 2。电子邮件 3 和 4 仍然存在。
A possible solution
一个可能的解决方案
Probably not the best one, but it should work.
可能不是最好的,但它应该有效。
Add a remove
boolean to the Email
class :
remove
向Email
类添加一个布尔值:
...
public class Email {
...
private boolean remove; // Set this flag to true to indicate that
// you want to remove the person.
...
}
In the save
method of your controller, remove the emails that have remove
set to true.
在save
您的控制器的方法中,删除已remove
设置为 true的电子邮件。
Finally, in your JSP, add this hidden field :
最后,在您的 JSP 中,添加此隐藏字段:
<form:hidden path="emails[${status.index}].remove" />
And tell your Javascript to set the input value to true when the user clicks to delete the email.
并告诉您的 Javascript 在用户单击以删除电子邮件时将输入值设置为 true。
回答by ch4mp
Alternate solution to Jerome Dalbert one
Jerome Dalbert 的替代解决方案一
Jerome's solution should work (thanks again for clear answer and solution), but I didn't want to modify business model.
So here is the way I found out: mark HTML elements to remove using java-script and actually remove it using ajax calls at form submit (I initially avoided ajax to keep model unchanged until user submits, but this solutions preserves that requirement).
Jerome 的解决方案应该有效(再次感谢明确的答案和解决方案),但我不想修改业务模型。
所以这是我发现的方法:使用 java-script 标记要删除的 HTML 元素,并在表单提交时使用 ajax 调用实际删除它(我最初避免使用 ajax 以保持模型不变,直到用户提交,但此解决方案保留了该要求)。
JSP:
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" alt="dropEmail/${person.emails[status.index].id}"></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:
控制器:
@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++;
}
}
}