程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 用Spring MVC來處理向導式復雜表單

用Spring MVC來處理向導式復雜表單

編輯:關於JAVA

引子

我們在網上經常會碰到一些向導式的表單頁面,比如 第一步 >> 第二步 >> 第三步…、基本信息 >> 聯系方式 >> 興趣愛好…。它們都是按類型分到多個有序的頁面要你來完成填寫的。當然,你也可以把它們全部羅列在一個頁面裡,那樣用戶就會感覺茫茫然,分不清主次,可能不會很好的予以配合。

然而像 Struts、WebWork 那樣的 MVC 框架未提供類似的實現,都需自己采用某種方式來實現向導,可以用層的隱現方式,或逐步把填寫的部分數據放 Session 中,最後匯總處理。幸運的是 Spring MVC 考慮到了這種應用需求,它提供了一個 org.springframework.web.servlet.mvc.AbstractWizardFormController 來滿足你。

下面我們來參照 CSDN 的簡歷向導(界面如下) 來自己動手做個例子

詳細的步驟如下(如何創建 Spring MVC 工程在此不細說,本例中用的是 Spring 2.0,其他版本的 Spring 原理無甚差異,只是 Spring 2.0 開始有很方便使用的 form 標簽,而在 Spring 1.0 中要用 spring:bind 和 jstl 結合顯示數據著實顯得很寒碜,由此可見 Spring 1 的 MVC 還太不成熟)。本頁中列出的代碼有些非關鍵之處省去了,文後附上了本例完整的工程文件,可下載。

一. 定義好需求

為了使我們盡快掌握 AbstractWizardFormController 的使用,而不至讓具體業務所糾纏。故而,這裡要對 CSDN 的簡歷向導大大的簡化,簡化後的向導頁和每頁的填寫內容如下:

1. 求職意向(期望工作地點-必填,期望月薪)

2. 基本信息(姓名-必填、手機號碼-必填)

3. 工作經歷(單個文本框錄入)

4. 項目經驗(單個文本框錄入)

5. 完成頁,Congratulations

每頁操作完之後,點擊“下一步”按鈕進到下一頁面,同時要對標識為必填項進行非空驗證。用戶也可以點“上一步”按鈕重填上一頁面的信息。用戶可以中途點擊“取消”按鈕取消向導。最後在項目經驗向導頁面,點擊“完成”按鈕處理表單數據,成功後顯示 Congratulations 頁。

二. 定義接收表單數據的 Command 類(Resume)

這裡我們定義為 com.unmi.bean.Resume,代碼如下:

package com.unmi.bean;
public class Resume {
private String workPlace; //期望工作地點
private int salary; //期望薪水
private String name; //姓名
private String mobile; //手機號碼
private String experience; //工作經歷
private String projects; //項目此驗
//...... 相應的 getter/setter 方法此處略去
}

三. 創建向導控制器(ResumeWizardController)

package com.unmi.webapp.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractWizardFormController;
import com.unmi.bean.Resume;
/**
* 處理簡單向導表單的控制類
* @author Unmi
*/
public class ResumeWiardController extends AbstractWizardFormController {

public ResumeWiardController(){
setCommandClass(Resume.class); //設置命令類
}
//處理表單數據
protected ModelAndView processFinish(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
//完成表單時獲取到所有填寫數據,調用業務類來處理
Resume resume = (Resume)command;
//resumeService.submitResume(resume);

return new ModelAndView("congratulations"); //完了後,轉到祝賀頁面
}
/**
* 向導中每一頁面都要調用的驗證方法,用 page 識別當前面
*/
protected void validatePage(Object command, Errors errors, int page) {
ResumeValidator validator = (ResumeValidator)getValidator();
if(page == 0){//驗證求職意向頁面
validator.validateWorkplace("workPlace", errors);
}
else if(page == 1){//驗證檔基本信息頁面
validator.validateName("name", errors);
validator.validateMobile("mobile", errors);
}
}
}

四. 還是提一下 web.xml 的配置

在 web.xml 增加了 Spring 的DispatchServlet 來處理 /*.html 的 URL,Servlet Name 為 resume

<servlet>
<servlet-name>resume</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resume</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

五. Spring 配置文件(resume-servlet.xml)

因為上面的 Servlet Name 為 resume,所以可以直接在 WEB-INF 目錄中直接配置一個文件名為 resume-servlet.xml 的 Spring 配置文件。這是默認行為,當然,你也可以為 DispatcherServlet 用 contextConfigLocation 屬性來指定配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean name="/createResume.html" class="com.unmi.webapp.controller.ResumeWiardController">
<property name="resumeService">
<ref bean="resumeService"/>
</property>
<property name="pages">
<list>
<value>intention</value>
<value>baseinfo</value>
<value>experience</value>
<value>projects</value>
<value>congratulations</value>
</list>
</property>
<property name="validator">
<bean class="com.unmi.webapp.validator.ResumeValidator"></bean>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/resume/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>

上面對 ResumeWizardController 用 pages 指定了一系列的邏輯視圖名,他們依序有一個索引號(以 0 為基數),後面會用到。這些邏輯面在運行時會依據 InternalResourceViewResolver 解析成對應的 jsp 文件名,例如,intention ->/resume/intention.jsp,其余類同。

六. 分步顯示向導中的表單頁面

任何向導控制器顯示的第一個頁面都是 pages 屬性中列表的第一個頁面。其後為了判斷接下來的是哪個頁面, AbstractWizardFormController 詢問它的 getTargetPage() 方法。這個方法返回一個整數,它就是 pages 和中設置頁面列表的索引值。

getTargetPage() 方法的缺省實現是根據請求中的一個參數來決定下一步是哪個頁面的,這個參數以 "_target" 開頭,以數結尾。getTargetPage() 只取這個數字作為頁面列表的索引。一般我們會把該參數命名給相應的提交按鈕。例如我們在第二個頁面 /resume/baseinfo.jsp 中的“上一步”,“下一步” 按鈕的 html 代碼這樣寫:

<form action="/createResume.html" method="post">
......
<input type="submit" value="上一步" name="_target0">
<input type="submit" value="下一步" name="_target2">
</form>

七. 完成或取消向導

那麼AbstractWizardFormController 又是如何知道你點擊的是完成按鈕,要調用 processFinish() 方法處理表單數據,或是點了取消按鈕,要調用 processCancel() 來作些清理工作或作部分數據處理呢?它也是依據於特殊的請求參數,它們分別是 “_finish” 和 “_cancel”。因此,相應的 html 代碼就要寫成:

<form action="/createResume.html" method="post">
......
<input type="submit" value="完成" name="_finish">
<input type="submit" value="取消" name="_cancel">
</form>

八. 每次驗證一個向導表單

對於這種向導式頁面,如果你仍是在最後點完成按鈕來驗證所有表單數據的話,一旦某個數據有問題,你將很難定位是在哪個向導頁輸入的,並且轉向到哪個出錯頁面也麻煩。所以我們需要在每填完一個表單,點擊 “下一步” 或“完成”按鈕時立即就對當前表單數據進行驗證。

AbstractWizardFormController 在每次頁面跳轉時會調用它的 validatePage() 方法。而 validatePage() 方法缺省實現是空的,這要留給你來實現。AbstractWizardFormController 中有兩個重載的 validatePage() 方法,代碼分別如下:

protected void validatePage(Object command, Errors errors, int page) {
}
protected void validatePage(Object command, Errors errors, int page, boolean finish) {
validatePage(command, errors, page);
}

你可以選擇實現其中一個方法來對表單進行驗證。如果你希望在點擊“完成” 按鈕時,能作一些特別的驗證,例如,如多個表單的相關聯數據進行一致性檢查,那你就應該實現帶有 boolean finish 參數的 validatePage() 方法。

最好是把驗證邏輯單獨寫在一個實現了 Validator 接口的驗證類中,通過 validator 屬性配置給你的 ResumeWizardFormController,然後在 validatePage() 方法中取到這個 validator,再調用其中的驗證方法 validateXXX()。例如我們前面配置在 resume-servlet.xml 中的 com.unmi.webapp.validator.ResumeValidator。

注意,對於 AbstractWizardFormController,它不會調用配置給它的 validator 的標准的validate() 方法。在點擊“下一步”按鈕,即向導正向走時,若驗證不通過,則停留在當前頁,等待重新輸入。然而,當點擊“上一步”按鈕時,也就是頁面索引號遞減,向導逆向走時,同樣會要求對當前頁面輸入進行驗證,只不過這種情況下驗證即使不能通過也不會停留在當前頁,仍會轉向到上一頁面。

這時候,我們在 ResumeWizardController 的 validatePage(Object command, Errors errors, int page) 就可以參照這麼寫了:

protected void validatePage(Object command, Errors errors, int page) {
Resume resume = (Resume) command;
ResumeValidator validator = (ResumeValidator)getValidator();
if(page == 1){//判斷所在的頁面來調用相應的驗證方法
validator.validateName("name", errors);
}
}

對於另一個版本的 validatePage(Object command, Errors errors, int page, boolean finish) 方法,那就是:

protected void validatePage(Object command, Errors errors, int page,
boolean finish) {
Resume resume = (Resume) command;
ResumeValidator validator = (ResumeValidator)getValidator();
if(page == 1){//判斷所在的頁面來調用相應的驗證方法
validator.validateName("name", errors);
}

if(finish){//如果是完成向導時,進行自己的驗證,這裡直接調用了標准的 validate() 方法
validator.validate(command,errors);
}
}

九. 完整的工程代碼

下載地址:http://www.blogjava.net/Files/Unmi/SpringWizardForm.rar

本工程未去處理有請求參數 _cancel 時,執行 ResumeWizardController.processCancel() 方法的情形,如果你有這樣的需求的話可自己去完善。對 ResumeWizardController,也是只實現了 validatePage(Object command, Errors errors, int page) 這個版本的方法。

解壓到 Tomcat 下即能運行,浏覽 http://localhost:8080/SpringWizardForm 點鏈接進到向導。包含源代碼和所需的 jar 文件。使用的是 Spring 2.0,jsp 頁面中用 spring-form 標簽。支持國際化,從資歷源文件中獲取驗證錯誤信息在輸入框下方顯示。

十. 運行效果

把整個操作過程做成了一個 Gif 動畫來展示,能使你一目了然。只恐怕這精彩的部分放在後頭,可能鮮有人有此等耐心把滾動條拉至此處。

說明:

1. 浏覽 http://localhost:8080/SpringWizardForm,點鏈接進到向導頁

2. 操作中測試到了每一個驗證的效果,期望工作地點、姓名和手機號碼不能為空

3. 驗證不通過時,從資源文件中取出錯誤信息,顯示在相應輸入框之下。並且頁面仍留在當前頁

4. 在向導的進行中,點“上一步”,“下一步”按鈕時,只要填寫提交過的數據一直保留

5. 所有數據填完後,點擊“完成”按鈕,業務類處理整個表單數據(後台會打印出 resume 信息),頁面顯示恭喜.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved