Struts2內置對dojotoolkit的支持

@TODO 基于Ajax標簽做更詳細的使用講解,由于dojo ajax標簽在struts2中還處于試驗階段,所以我們主要講解dojo在不使用struts2內置功能的情況下怎么和struts2結合使用。

 

Dojotoolkit介紹

Dojotoolkit是當前比較流行的JavaScript框架,dojo主要提供了以下功能:Java Script Web組件(例如TreeGridButtonMenu等),事件處理框架,Ajax異步調用等功能,非常好用。

Dojo現(xiàn)在主要有兩個版本:

0.4.3 現(xiàn)在流行的版本,優(yōu)點是,功能非常強大,非常穩(wěn)定,缺點是類庫本身比較低,執(zhí)行效率較低

0.9 beta是新的版本,是為了保留0.4.3的有點同時提升性能而重寫的一個版本,但是現(xiàn)在還處在beta階段,bug相會還比較多。但是0.9系列絕對是將來的發(fā)展方向。

 

我們主要以0.9 beta版本為講解對象,主要講解以下內容:Widget組件,事件處理,Ajax等功能。但是情大家注意,dojo的功能遠不止這么多。

 

Dojo安裝

1 從下述網站下載dojo0.9 beta壓縮文件

http://dojotoolkit.org/downloads

2 在你的Web項目目錄中建立一個名字叫做js的目錄(名字任意,這里叫js

3 將壓縮文件在上面建立的目錄中解壓縮,得到如下文件結構:


4 打開瀏覽器,在地址欄中輸入如下內容:

  http://hostname:port/js/dojo-0.9.0beta/dijit/themes/themeTester.html

如果看到如下頁面說明安裝成功:

themeTester.png

 

Dojo Widget 組件

我們回頭看/themeTester.html 畫面,上面的組件要比HTML默認的組件漂亮很多。他們都是使用dojowidget(又叫dijit)來完成的。

Dojo widget在目錄dijit中。

 

下面我們使用一個小例子還逐步的說明dijit的使用方法,通過如下步驟,我們將todoinsert畫面改編為dijit組件。

 

我們有一個小小的Todo輸入的頁面,靜態(tài)的效果如下:


這里有很多地方不是很方便,例如:

1 日期能不能出現(xiàn)一個日歷選擇對話框,只要選擇就好?

2 日期能不能自動校驗,例如如果輸入,2007/02/29可以判斷為錯誤日期?

3 時間需要校驗,需要校驗格式(HH:mm:ss)和數(shù)字內容例如不會有25:89:60

4 下拉列表能不能支持手工編寫?能不能支持根據(jù)已經輸入的值自動過濾選擇項?

5 能否讓系統(tǒng)提示必須填入項目?

 

通過使用dojodijit組件這些都可以做到,并且dijit組件有兩種使用方式:標簽和JS編程動態(tài)實現(xiàn)。

無論那種方式實現(xiàn)都需要了解一些基本的步驟:

第一步,由于dojoJavaScript編寫的,所以第一步必須首先引用dojo框架的JavaScript,同時dojo提供了一些初始化配置,可以在引用dojo的時候同時配置,代碼如下:

<script type="text/javascript"

    src="<%= request.getContextPath()%>/dojo-0.9.0beta/dojo/dojo.js"

    djConfig="isDebug: true, parseOnLoad: true">

</script>

 

其中src指向的是dojoweb項目中的存放路徑。

djConfigdojo追加的自定義屬性,在HTML標準里并沒有。使用djConfig可以做一些基本的配置,isDebug表示了當前頁面中使用的dojo內容是否處在調試狀態(tài),在調試狀態(tài)可以輸出更多的內容,一般開發(fā)的時候我么選擇trueparseOnLoad表示所有的dijit組件是否在html頁面裝載的時候自動的做展現(xiàn)(Render)處理,使用dijit組件的時候我們選擇true,沒有使用的時候使用false可以獲得更好的性能。

第二步,引用需要使用的dojo類,這一步有些像Java中的import,注意添加引用的類會增加頁面加載是JavaScript的下載量,所以應該盡量的精簡引用的dojo對象。代碼為:

<script type="text/javascript">
        dojo.require("dijit.form.ValidationTextbox");
        dojo.require("dijit.form.DateTextbox");
        dojo.require("dijit.form.ComboBox");
        dojo.require("dijit.form.Button");
        dojo.require("dijit.form.Textbox");
        dojo.require("dijit.form.NumberTextbox");
        dojo.require("dijit.form.CurrencyTextbox");
        dojo.require("dojo.currency");
        dojo.require("dojo.date.locale");
        dojo.require("dojo.parser");
</script>

上述的代碼說明了我們需要在后續(xù)的代碼中使用的dojo組件。

 

第三步,引用需要使用的CSS文件,由于dojo使用CSS實現(xiàn)了主題的概念,所以我們在使用dojo的時候一定要選擇合適的主題。代碼為:

<style type="text/css">
@import "/teamware/dojo-0.9.0beta/dojo/resources/dojo.css";
@import "/teamware/dojo-0.9.0beta/dijit/themes/dijit.css";
@import "/teamware/dojo-0.9.0beta/dijit/themes/tundra/tundra.css";
</style>

當然,也可以加入自己的內容,自定以主題。默認的主題時tundra主題。

另外需要注意,使用主題的時候需要在htmlbody元素上使用class屬性,代碼為:

<body class="tundra">

 

第四步(標簽),使用dojodijit組件。

所有的組件都有一個dojo自定義的屬性dojoTypedojoType決定了這個組件的dijit類型。例如:
 
日期組件
<input id="startDate" type="text" name="todo.startDate" 
        class="medium" value="2005-12-30"
        dojoType="dijit.form.DateTextbox"
        constraints={min:'2004-01-01',max:'2006-12-31'}
        required="true"
        promptMessage="mm/dd/yyyy"
        invalidMessage="Invalid date. Use mm/dd/yyyy format." />

這里dojoType=”dijit.form.DateTextbox”,表示這個組件的類型為DateTextbox類型,具體表現(xiàn)形式為:

 

時間組件:

<input id="startTime" type="text" name="todo.startTime" 
        class="medium" value="5:45:00 pm"                             dojoType="dijit.form.ValidationTextbox"
        validator="dojo.date.locale.parse"
        constraints={formatLength:'medium',selector:'time'}
        trim="true"
        required="true"
        invalidMessage="Invalid time." />

 

具體表現(xiàn)形式為:

 

第四步(JS編程動態(tài)實現(xiàn)),使用dojodijit組件。

在使用Struts2的過程中,為了方便操作,我們一般使用Struts2的內置標簽,例如:<s:text/>等。但是使用這些標簽有一個問題,這些標簽都是Struts2的標簽,在定制這些標簽的時候沒有考慮到dojo,所以這些標簽中不能使用dojoType屬性,所以要想在Struts2中要想使用dojo,就得:要么放棄這些好用的Struts2標簽,要么,就是我們要講的使用JS動態(tài)的使用dojo

例如,我們有了如下的JSP頁面(只講述form部分),其中使用了struts2標簽:

<s:form action="insert" namespace="/todo">

    <s:textfield id="startDate"

name="todo.startDate" label="Start Date" />

    <s:textfield id="startTime"

name="todo.startTime" label="Start Time" />

    <s:textfield id="endDate"

name="todo.endDate" label="End Date" />

    <s:textfield id="endTime"

name="todo.endTime" label="Emd Time" />

    <s:select list="%{#{'完成':'完成','未完成':'未完成'}}"

name="status" id="status" label="Status">

</s:select>

    <s:select list="%{#{'':'','':'','':''}}"

name="priority"

       id="priority" label="Priority">

</s:select>

    <s:select list="%{#{'個人':'個人','商務':'商務'}}"

name="category"

       id="category" label="Category"></s:select>

    <s:textfield id="title"

name="todo.title" label="Title"></s:textfield>

    <s:textarea id="content"

name="todo.content" label="Content" cols="20"

       rows="10"></s:textarea>

    <s:submit id="insertButton" label="insert" />

</s:form>

 

那么我們如何使用JavaScript來動態(tài)的使用Dojo呢?

基本原理如下:

(1)      構造組件需要使用的dojo屬性

(2)      找到需要套用dojo組件的HTML 元素,使用前面定義的屬性建立新的替代組件,

 

例如,現(xiàn)在要將

<s:textfield id="startDate"

name="todo.startDate" label="Start Date" />

替換為dijit.form.DateTextbox類型,代碼為:

var paramsStartDate = {

       required: "true",

       constraints: {selector: "date", formatLength: "short",

datePattern: "yyyy-MM-dd", timePattern: "HH:mm:ss", locale: "zh-cn"}

}

var startDateText = new dijit.form.DateTextbox(paramsStartDate,

                                   document.getElementById("startDate"));

首先我們定義了一個Map型的對象paramsStartDate用來存儲我們在構建dijit組件時使用的屬性和值。之后我們使用dijit.form.DateTextbox的構造方法來創(chuàng)建dojo類型的組件。

但是需要注意一點,上述代碼必須在HTML頁面load的時候執(zhí)行,否則無法正常創(chuàng)建dojo組件,這里我們使用了dojo對象的addOnLoad方法吧這段代碼加入到HTML頁面load時運行的代碼中。全部的JavaScript代碼如下:

<script type="text/javascript">

       dojo.addOnLoad(

              function () {

                    

                     var paramsStartDate = {

                            required: "true",

                            constraints: {selector: "date", formatLength: "short",

datePattern: "yyyy-MM-dd", timePattern: "HH:mm:ss",

locale: "zh-cn"}

                     }

                     var startDateText = new dijit.form.DateTextbox(paramsStartDate,

                                   document.getElementById("startDate"));

                    

                     /* init start time */

                     var paramsStartTime = {

                            value: "08:30:00",

                            validator: dojo.date.locale.parse,

                            constraints: {formatLength: "short", selector: "time",

timePattern: "HH:mm:ss"},

                            trim: "true",

                            required: "true",

invalidMessage: "Invalid time. Use HH:mm:ss where HH is 00 - 23 hours."

                     }

                     var startTimeText =

                            new dijit.form.ValidationTextbox(paramsStartTime,

                                   document.getElementById("startTime"));

             

                     /* init title */

                     var paramsTitleText = {

                            required: "true",

                            promptMessage: "Enter a Title"

                     }

                    

                     var titleText =

                            new dijit.form.ValidationTextbox(paramsTitleText,

                                   document.getElementById("title"));

                    

              }     

       );

</script>

  

 
Dojo事件處理
傳統(tǒng)的使用JavaScript Event的方式和dojo.connect的方式
JavaScript中使用事件的方式為:
<script type="text/javascript">
    function foo() {
       alert("Hello!");
    }
</script>
 
<button id="firstButton" onclick="foo();" >Hello!</button>
 
上述代碼表示firstButton被點擊(onclick)的時候,觸發(fā)foo()方法。
 
使用上述方式很簡單,但是存在不好的地方,所有的畫面表示內容(例如<button>標簽)會和事件處理(onclick=”foo();”)關聯(lián)起來。當畫面事件處理比較簡單的時候,這個缺點體現(xiàn)不出來,但是當畫面時間比較多,并且經常需要調整JavaScript方法和表示內容的時候就會暴露出來修改比較困難的問題。
 
Dojo.connect方法
可以使用connectJavaScript方法和表示組件聯(lián)合起來,例如:
dojo.addOnLoad(
    function() {
       dojo.connect(dojo.byId("firstButton"), "onclick", foo);
    }
);
上述的代碼表示將firstButtononclick事件和foo方法聯(lián)系起來,當firstButton被點擊的時候會觸發(fā)foo方法。
這里就解決了畫面表示內容(例如<button>標簽)和事件處理之間關聯(lián)的問題。我們可以在onLoad方法內部將所有的關聯(lián)鏈接起來。
 
Event Object 說明
我們接著dojo.connect方法說,能不能在JavaScript方法內部看到事件的詳細信息?
我們把事件的相關信息認為Event Object對象,例如,在做方法的時候,我們可以使用一個dojo的內置對象叫做EventObject
 
EventObject屬性:
event.target —  觸發(fā)事件的HTML元素(the element that generated the event
event.currentTarget — 當前的Targetthe current target
event.layerX — currentTarget相關的X坐標( the x coordinate, relative to the
event.currentTarget
event.layerY — currentTarget相關的Y坐標( the y coordinate, relative to the
                                                                                             event.currentTarget
event.pageX — 當前View PointX坐標(the x coordinate, relative to the view port
event.pageY — 當前View PointY坐標the y coordinate, relative to the view port
event.relatedTarget — 對于onmouseover and onmouseout 時間,指鼠標進入或者退出的對象。(For onmouseover and onmouseout, the object that the mouse pointer is moving to or out of
event.charCode — 針對于keypress時間表示key code For keypress events, the character code of the key pressed
 
EventObject方法:
event.preventDefault — 阻止了時間的默認行為( prevent an event's default behavior (e.g., a link from loading a new page)
event.stopPropagation — 阻止了父Node的事件觸發(fā)( prevent an event from triggering a parent node's event
 
例如:
<script type="text/javascript">
 
function echo(event) {
        key = event.charCode;
        console.debug(event.charCode);
}
 
function foo(event) {
        dojo.stopEvent(event);
        console.debug("The link was clicked");
}
dojo.addOnLoad(function() {
        interactiveNode = dojo.byId("interactive");
        linkNode = dojo.byId("link");
        dojo.connect(interactiveNode, 'onkeypress', echo);
        dojo.connect(linkNode, 'onclick', foo);
});
</script>
<body>
        <a id="link">Dojo</a> is great.
        <form>
                <label for="infield"> Type some text: </label>
                <input id="interactive" type="text" name="infield">
        </form>
</body>
 
Dijit組件和Event
         @TODO
Topic
@TODO
 
 
DojoAjax異步調用
HelloWorld Dojo Ajax 程序
我們做一個畫面,在畫面中使用ajax從服務器中取得動態(tài)的內容,將這些內容顯示在頁面中。
首先我們在服務器上做一個txt文件,例如文件為ajax.txt,內容為:
Hello World!
This is a simple ajax text file.
 
然后做一個jsp畫面,其中有一個div內容從服務器上的ajax.txt文件取得,代碼為:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Ajax Hello World!</title>
<script type="text/javascript"
    src="<%= request.getContextPath()%>/dojo-0.9.0beta/dojo/dojo.js">
    <!-- -->
</script>
 
<script type="text/javascript">
    function hello() {   <!-- -->
      dojo.xhrGet( {
        // The following URL must match that used to test the server.
        url: "http://localhost:8080/teamware/test/ajax.txt",<!-- -->
        handleAs: "text"<!-- -->
 
        timeout: 5000, // Time in milliseconds
 
        // The LOAD function will be called on a successful response.
        load: function(response, ioArgs) {   <!-- -->
          dojo.byId("cargo").innerHTML = response;
          return response;
        },
 
       // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {  <!-- -->
          console.error("HTTP status code: ", ioArgs.xhr.status);
          return response;
          }
        });
      }
</script>
 
<script type="text/javascript">
    dojo.addOnLoad(hello); <!-- -->
</script>
 
</head>
<body>
   <div id="cargo" style="font-size: big"></div<!-- -->
</body>
</html>
 
說明:
① 引入dojo框架
② 把方法hello注冊到onload中,當畫面全部裝載以后運行該方法。
③ 顯示ajax.txt文件內容的div
④ Ajax方法,取得服務器ajax.txt的內容,在cargo div中顯示這些內容。
⑤ 指向ajax.txturl路徑。
⑥ 服務器文件類型
⑦ Ajax調用成功的情況下,調用的方法,包括將ajax.txt文件中的內容放在div
⑧ 錯誤處理
 
 


ExtJS教程- Hibernate教程-Struts2 教程-Lucene教程