Struts整合ExtJS以及遇到的一些問題(ModelDriven)

1準備工作:

除瞭平時引入的struts2的jar包以外,還需要引入struts2-json-plugin-2.1.8.1.jar;json-lib-2.1.jar這兩個包。

2.建立我們的model:User

packageedu.tstc.model;

 

public classUser {

private int id;

private String username;

private String password;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

}

 

 

3.建立我們的Action

這裡是簡單的演示struts-2與extjs的集成,所以全部的業務邏輯都放在Action中進行處理瞭。建立我們的LoginAction.當表單提交過來的時候我們也就能夠在LoginAction中拿到數據瞭。

packageedu.tstc.action;

 

import edu.tstc.model.User;

importcom.opensymphony.xwork2.ActionSupport;

 

public classLoginAction extends ActionSupport{

private boolean success;

private String message;

private User user;

public String execute()throws Exception{

if(user.getUsername().equals(admin)&&user.getPassword().equals(admin)){

this.success = true;

this.message = 你的賬號是:+user.getUsername()+密碼為:+user.getPassword();

}else{

this.success = false;

this.message = 對不起,未經授權的用戶不能登錄該系統!;

}

return SUCCESS;

}

 

public boolean isSuccess() {

return success;

}

 

public void setSuccess(boolean success) {

this.success = success;

}

 

public String getMessage() {

return message;

}

 

public void setMessage(String message) {

this.message = message;

}

 

public User getUser() {

return user;

}

 

public void setUser(User user) {

this.user = user;

}

}

Action類中必須指定一個boolean類型的變量success,以及一個可選的String類型的變量msg,前者用於向ExtJS反饋整個的處理結果是否成功,而後者則是用於向用戶提示信息。定義好這兩個變量後還需要生成setter和getter方法。整個Action類正常反饋SUCCESS即可。

 

4.配置我們的struts.xml,註意extends=”json-default”

 

 

-//Apache Software Foundation//DTD StrutsConfiguration 2.0//EN

https://struts.apache.org/dtds/struts-2.0.dtd>

 

 

 

 

 

result的type必須指定為json。一旦為某個Action指定瞭一個類型為json的Result,則該Result無須映射到任何視圖資源,因為JSON插件會負責將Action裡的信息序列化成JSON格式的字符串,並將該字符串返回給發出異步請求的瀏覽器頁面。

 

簡單的說JSON插件允許我們在客戶端頁面的JavaScript中一步調用Action,而且Action不再需要使用視圖資源去顯示該Action裡的狀態信息,而是由JSON插件負責將Action裡的狀態信息返回給調用頁面——通過這種方式,就可以完成Ajax交互。

 

同時還有兩個需要註意的地方:

1:在配置struts.i18n.encoding常量時需要指定為UTF-8編碼,這是因為Ajax的POST請求都是以UTF-8的方式進行編碼的;

2:在配置包時,需要繼承json-default,而不是原來的struts-default包,這是因為隻有該包下才有json類型的Result。

 

一旦我們將某個邏輯配置成為json類型,這就意味著該邏輯視圖無需指定物理視圖資源,因為json插件會將該Action序列化後發送給客戶端。

 

5.在web.xml文件中配置struts2

xmlns=https://java.sun.com/xml/ns/javaee

xmlns:xsi=https://www.w3.org/2001/XMLSchema-instance

xsi:schemaLocation=https://java.sun.com/xml/ns/javaee

https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd>

struts2

org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

 

 

struts2

/*

 

index.jsp

 

 

 

6.接下來是前臺的頁面,其中包括login.html(登陸的界面),login.js(javascript代碼),index.jsp(登陸成功後返回的界面)

Login.html

 

 

 

 

 

<script type=text/javascript
src=ext/adapter/ext/ext-base.js></script>

<script type=text/javascript
src=ext/ext-all-debug.js></script>

<script type=text/javascript
src=js/login.js></script>

 

 

 

 

 

 

 

Login.js

Ext.onReady(function(){

//使用表單提示

Ext.QuickTips.init();

Ext.form.Field.prototype.msgTarget =side;

//定義一個輸入表單

var simple = new Ext.FormPanel({

labelWidth:40,

baseCls:'x-plain',

defaults:{width:180},

items:[{

xtype:'textfield',

fieldLabel:'賬號',

name:'user.username',

allowBlank:false,

blankText:'賬號不能為空'

},{

xtype:'textfield',

inputType:password,

fieldLabel:密碼,

name:'user.password',

allowBlank:false,

blankText:密碼不能為空

}],

buttons:[{

text:提交,

type:submit,

handler:function(){

if(simple.form.isValid()){

Ext.MessageBox.show({

title:請等待,

msg:正在加載,

progressText:,

width:300,

progress:true,

closable:false,

animEl:'loding'

});

var f = function(v){

return function(){

var i = v/11;

Ext.MessageBox.updateProgress(i,'');

}

}

for(var i = 1; i < 13; i++){

setTimeout(f(i),i * 150);

}

//提交到服務器操作

simple.form.doAction(submit,{

url:Login.action,

method:post,

success:function(form,action){

document.location = 'index.jsp';

Ext.Msg.alert(登錄成功!,action.result.message);

},

failure:function(form, action){

Ext.Msg.alert('登錄失敗',action.result.message);

}

});

}

}

},{

text:重置,

handler:function(){

//重置表單

simple.form.reset();

}

}]

});

//定義窗體

var _window = new Ext.Window({

title:登錄窗口,

layout:fit,

width:280,

height:150,

plain:true,

bodyStyle:padding:10px,

maximizable:false,

closeAction:close,

closable:false,

collapsible:true,

plain:true,

buttonAlign:center,

items:simple

});

_window.show();

});

 

Index.jsp

<%@ page language=java contentType=text/html;charset=GB18030

pageEncoding=GB18030%>

 

 

 

 

 

 

 

恭喜你登陸成功瞭!

 

 

 

 

關於調試:

為瞭確定編寫的Action是否向頁面正確的返回瞭JSON數據,我們在調試時可以嘗試直接訪問Action的方式,來查看返回的JSON數據是否正確;

 

 

可能遇到的錯誤:

使用struts時,前臺向後臺傳值有三種方式:

1:在Action中定義與前臺頁面輸入變量一樣變量名的變量,如:

public class UserAction {

private int id;

private String username;

private String password;

private int age;

private String address;

 

public String add(){

 

User user = new User();

user.setId(id);

user.setUsername(username);

user.setPassword(password);

user.setAge(age);

user.setAddress(address);

 

new UserManager().addUser(user);

 

return success;

}

 

public int getId() {

return id;

}

public void setId(int id){

this.id =id;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username =username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password =password;

}

public int getAge() {

return age;

}

public void setAge(int age){

this.age =age;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address =address;

}

}

這種方式的缺點是,如果需要輸入的屬性非常多,那麼Action將變得非常臃腫,

 

2:將一個包含多個屬性的對象定義到Action中去,在前臺頁面中通過屬性來給對象賦值(如user);

Action:

public class UserAction {

 

private User user;

 

public String add(){

 

new UserManager().addUser(user);

 

return success;

}

 

public User getUser() {

return user;

}

 

public void setUser(User user) {

this.user =user;

}

}

前臺輸入頁面:

 

 

username:

password:

age:

address:

 

 

這種做法的小缺點是前臺輸入頁面表單域的命名變長。

 

3:第三種做法是利用ModelDriven機制,讓UserAction實現一個ModelDriven接口,同時實現接口中的方法:getModel()。如下所示:

後臺Action:

public class UserAction implements ModelDriven{

 

private User user;

 

@Override

public Object getModel() {

if(user == null){

user = new User();

}

return user;

}

 

public String add(){

 

new UserManager().addUser(user);

 

return success;

}

 

public User getUser() {

return user;

}

 

public void setUser(User user) {

this.user = user;

}

}

前臺頁面:

 

 

username:

password:

age:

 

 

這種做法的好處是後臺代碼量少,無多餘屬性定義,前臺表單域的命名也很短。

 

但是,這裡有一個但是。

在struts和ExtJS集成的過程中,如果你的Action類實現瞭這個接口,而且你還在你的Action類中定義瞭boolean類型的success變量和String類型的msg,

但是這兩個屬性你的實體類(如User類)中是沒有的,因此,當Action對如登錄這樣的請求處理結束後,返回給請求的發出頁面的JSON字段就會不包含在Action中定義的success變量和msg變量。

原因是你實現ModelDriven接口時,也實現瞭getModel()方法,給方法返回一個對象,比如user對象,這樣前臺提交過來的參數,隻要名字和user對象中setter方法的名字對應就會自動映射,並且會通過user對象中定義的各個getter方法將這些屬性值壓入valueStack棧中,前臺可以通過來獲得。

換句話說實現瞭這個接口一個就隻會把user對象中有的屬性壓入棧中,而在Action中定義的變量是不會壓入棧中,也就是不會返回給請求的發起者,這樣的話前臺ExtJS頁面就不會得到包括success(不可缺少)和msg(可有可無,用於存儲提示信息)的JSON數據,回調時也就不會執行success所對應的方法,而是不斷指定failure所對應的方法。

 

總結一下:

1:前臺定義的表單域如果名字與User對象裡的變量名相同便能夠完成前臺到後臺的正常賦值;

2:Action處理結束後返回給頁面的所有屬性就User對象裡的屬性,即User對象裡有什麼屬性就返回給頁面什麼屬性,在Action中定義的變量不會返 回給頁面;

3:假如在Action中定義一個與前臺表單域名字相同的變量,該變量不會被賦值,因為隻會給getModel()方法返回的對象裡的屬性賦值;

 

 

因此選擇去掉ModelDriven的實現,或者選擇在User對象中加入success變量都可以完成Struts與ExtJS的集成,使得能夠正常執行success所對應的函數。但是後一種方法在User對象中加入瞭success變量又破壞瞭User對象。

 

 

 

 

 

 

 

發佈留言