本节介绍SpringMVC中的表单,demo演示访问一个表单提交页面,填写表单的内容后使用jQuery的Ajax提交表单,将返回的文本信息显示出来。
记得在Struts1中有个FormBean的东西封装表单内容,在SpringMVC中也有,只不过SpringMVC更松耦合,只要写一个POJO就可以了,而不需要继承框架关联的类,看一下这个FormBean(只列出了主要属性):
public class FormBean {
@NotEmpty
private String name;
@Min(21)
private int age;
@DateTimeFormat(iso=ISO.DATE)
@Past
private Date birthDate;
@MaskFormat("(###) ###-####")
private String phone;
@NumberFormat(pattern="$###,###.00")
private BigDecimal currency;
@NumberFormat(style=Style.PERCENT)
private BigDecimal percent;
private InquiryType inquiry;
private String inquiryDetails;
private boolean subscribeNewsletter;
private Map<String, String> additionalInfo;
...
}
需要一个Controller,FormController:
package org.springframework.samples.mvc.form;
import javax.validation.Valid;
import org.springframework.mvc.extensions.ajax.AjaxUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@RequestMapping("/form")
@SessionAttributes("formBean")
public class FormController {
// Invoked on every request
@ModelAttribute
public void ajaxAttribute(WebRequest request, Model model) {
model.addAttribute("ajaxRequest", AjaxUtils.isAjaxRequest(request));
}
// Invoked initially to create the "form" attribute
// Once created the "form" attribute comes from the HTTP session (see @SessionAttributes)
@ModelAttribute("formBean")
public FormBean createFormBean() {
return new FormBean();
}
@RequestMapping(method=RequestMethod.GET)
public void form() {
}
@RequestMapping(method=RequestMethod.POST)
public String processSubmit(@Valid FormBean formBean, BindingResult result,
@ModelAttribute("ajaxRequest") boolean ajaxRequest,
Model model, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return null;
}
// Typically you would save to a db and clear the "form" attribute from the session
// via SessionStatus.setCompleted(). For the demo we leave it in the session.
String message = "Form submitted successfully. Bound " + formBean;
// Success response handling
if (ajaxRequest) {
// prepare model for rendering success message in this request
model.addAttribute("message", message);
return null;
} else {
// store a success message for rendering on the next request after redirect
// redirect back to the form to render the success message along with newly bound values
redirectAttrs.addFlashAttribute("message", message);
return "redirect:/form";
}
}
}
FormController只有一个@RequestMapping,通过GET,POST来区分是访问表单页面(GET),还是提交表单(POST)。在这个类中有:
@ModelAttribute("formBean")
public FormBean createFormBean() {
return new FormBean();
}
它表示访问"http://localhost:8080/web/form/"时,就初始化一个叫formBean名字的属性放在Model中。在类的开头有:
@SessionAttributes("formBean")
它表示Model中的这个formBean在session的范围内有效,想一下,session是跟浏览器窗口关联的,窗口关闭,session就失效,所以每次打开一个新的表单页面都会生成一个新的formBean,另外填写完表单内容以POST方式提交后,formBean会根据提交的参数自动设置FormBean的值。
@ModelAttribute
public void ajaxAttribute(WebRequest request, Model model) {
model.addAttribute("ajaxRequest", AjaxUtils.isAjaxRequest(request));
}
该配置会导致每次访问"http://localhost:8080/web/form/",都会设置一个ajaxRequest值(因为没有象formBean一样设置ajaxRequest的范围,默认的范围为Request级,每次请求都执行)。之前说过填好表单后通过jQuery的Ajax提交表单,通过这个配置,就知道请求是否是Ajax请求。
@RequestMapping(method=RequestMethod.GET)
public void form() {
}
访问"http://localhost:8080/web/form/",返回到"webapp/WEB-INF/views/form.jsp"。
@RequestMapping(method=RequestMethod.POST)
public String processSubmit(@Valid FormBean formBean, BindingResult result,
@ModelAttribute("ajaxRequest") boolean ajaxRequest,
Model model, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return null;
}
// Typically you would save to a db and clear the "form" attribute from the session
// via SessionStatus.setCompleted(). For the demo we leave it in the session.
String message = "Form submitted successfully. Bound " + formBean;
// Success response handling
if (ajaxRequest) {
// prepare model for rendering success message in this request
model.addAttribute("message", message);
return null;
} else {
// store a success message for rendering on the next request after redirect
// redirect back to the form to render the success message along with newly bound values
redirectAttrs.addFlashAttribute("message", message);
return "redirect:/form";
}
}
processSubmit第一个参数formBean封装了页面提交的表单参数,注意到它前面有一个@Valid,因此有第二个参数BindingResult result, 该参数可以知道表单是否验证通过。第三个参数获取Model中的属性值"ajaxRequest",在方法体中,判断ajaxRequest,如果是,将返回"webapp/WEB-INF/views/form.jsp",如果不是,将它重定向到一个jsp,当然,这个条件在本例子中没有使用到。
最后看看非常复杂的form.jsp,该jsp使用了大量的SpringMVC的标签:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ page session="false" %>
<c:if test="${!ajaxRequest}">
<html>
<head>
<title>forms | mvc-showcase</title>
<link href="<c:url value="/resources/form.css" />" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="<c:url value="/resources/jquery/1.6/jquery.js" />"></script>
</head>
<body>
</c:if>
<div id="formsContent">
<h2>Forms</h2>
<p>
See the <code>org.springframework.samples.mvc.form</code> package for the @Controller code
</p>
<form:form id="form" method="post" modelAttribute="formBean" cssClass="cleanform">
<div class="header">
<h2>Form</h2>
<c:if test="${not empty message}">
<div id="message" class="success">${message}</div>
</c:if>
<s:bind path="*">
<c:if test="${status.error}">
<div id="message" class="error">Form has errors</div>
</c:if>
</s:bind>
</div>
<fieldset>
<legend>Personal Info</legend>
<form:label path="name">
Name <form:errors path="name" cssClass="error" />
</form:label>
<form:input path="name" />
<form:label path="age">
Age <form:errors path="age" cssClass="error" />
</form:label>
<form:input path="age" />
<form:label path="birthDate">
Birth Date (in form yyyy-mm-dd) <form:errors path="birthDate" cssClass="error" />
</form:label>
<form:input path="birthDate" />
<form:label path="phone">
Phone (in form (###) ###-####) <form:errors path="phone" cssClass="error" />
</form:label>
<form:input path="phone" />
<form:label path="currency">
Currency (in form $#.##) <form:errors path="currency" cssClass="error" />
</form:label>
<form:input path="currency" />
<form:label path="percent">
Percentage (in form ##%) <form:errors path="percent" cssClass="error" />
</form:label>
<form:input path="percent" />
</fieldset>
<fieldset>
<legend>Inquiry</legend>
<form:label path="inquiry">
Type (select one)
</form:label>
<form:select path="inquiry">
<form:option value="comment">Comment</form:option>
<form:option value="feedback">Feedback</form:option>
<form:option value="suggestion">Suggestion</form:option>
</form:select>
<form:label path="inquiryDetails">
Details
</form:label>
<form:textarea path="inquiryDetails" />
</fieldset>
<fieldset class="checkbox">
<legend>Request Additional Info</legend>
<label><form:checkbox path="additionalInfo[mvc]" value="true" />on Spring MVC</label>
<label><form:checkbox path="additionalInfo[java]" value="true" />on Java (4-ever)</label>
</fieldset>
<fieldset class="radio">
<legend>Subscribe to Newsletter?</legend>
<label><form:radiobutton path="subscribeNewsletter" value="true" />Yes</label>
<label><form:radiobutton path="subscribeNewsletter" value="false" /> No</label>
</fieldset>
<p><button type="submit">Submit</button></p>
</form:form>
<script type="text/javascript">
$(document).ready(function() {
$("#form").submit(function() {
$.post($(this).attr("action"), $(this).serialize(), function(html) {
$("#formsContent").replaceWith(html);
$('html, body').animate({ scrollTop: $("#message").offset().top }, 500);
});
return false;
});
});
</script>
</div>
<c:if test="${!ajaxRequest}">
</body>
</html>
</c:if>