第十四章 集成Dojotoolkit(Ajax框架)
Struts2内置对dojotoolkit的支持
@TODO 基于Ajax标签做更详细的使用讲解,由于dojo ajax标签在struts2中还处于试验阶段,所以我们主要讲解dojo在不使用struts2内置功能的情况下怎么和struts2结合使用。
Dojotoolkit介绍
Dojotoolkit是当前比较流行的JavaScript框架,dojo主要提供了以下功能:Java Script Web组件(例如Tree,Grid,Button,Menu等),事件处理框架,Ajax异步调用等功能,非常好用。
Dojo现在主要有两个版本:
<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False"><span lang="EN-US">0.4.3</span></chsdate> 现在流行的版本,优点是,功能非常强大,非常稳定,缺点是类库本身比较低,执行效率较低
0.9 beta是新的版本,是为了保留<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False"><span lang="EN-US">0.4.3</span></chsdate>的有点同时提升性能而重写的一个版本,但是现在还处在beta阶段,bug相会还比较多。但是0.9系列绝对是将来的发展方向。
我们主要以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-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">0.9.0</chsdate>beta/dijit/themes/themeTester.html
如果看到如下页面说明安装成功:
<!--[if gte vml 1]><v:shape id="_x0000_i1026" type="#_x0000_t75" alt="themeTester.png" title=""View: themeTester.png"" style='width:414pt;height:291.75pt' o:button="t"> <v:imagedata src="file:///C:\DOCUME~1\mazhao\LOCALS~1\Temp\msohtml1\01\clip_image003.png" o:href="http://dojotoolkit.org/files/themeTester.png" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
Dojo Widget 组件
我们回头看/themeTester.html 画面,上面的组件要比HTML默认的组件漂亮很多。他们都是使用dojo的widget(又叫dijit)来完成的。
Dojo widget在目录dijit中。
下面我们使用一个小例子还逐步的说明dijit的使用方法,通过如下步骤,我们将todo的insert画面改编为dijit组件。
我们有一个小小的Todo输入的页面,静态的效果如下:
<!--[if gte vml 1]><v:shape id="_x0000_i1027" type="#_x0000_t75" style='width:414.75pt;height:301.5pt'> <v:imagedata src="file:///C:\DOCUME~1\mazhao\LOCALS~1\Temp\msohtml1\01\clip_image005.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
这里有很多地方不是很方便,例如:
1 日期能不能出现一个日历选择对话框,只要选择就好?
2 日期能不能自动校验,例如如果输入,2007/02/29可以判断为错误日期?
3 时间需要校验,需要校验格式(HH:mm:ss)和数字内容例如不会有25:89:60。
4 下拉列表能不能支持手工编写?能不能支持根据已经输入的值自动过滤选择项?
5 能否让系统提示必须填入项目?
通过使用dojo的dijit组件这些都可以做到,并且dijit组件有两种使用方式:标签和JS编程动态实现。
无论那种方式实现都需要了解一些基本的步骤:
第一步,由于dojo是JavaScript编写的,所以第一步必须首先引用dojo框架的JavaScript,同时dojo提供了一些初始化配置,可以在引用dojo的时候同时配置,代码如下:
<script type="text/javascript"
src="<%= request.getContextPath()%>/dojo-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">0.9.0</chsdate>beta/dojo/dojo.js"
djConfig="isDebug: true, parseOnLoad: true">
</script>
其中src指向的是dojo在web项目中的存放路径。
djConfig是dojo追加的自定义属性,在HTML标准里并没有。使用djConfig可以做一些基本的配置,isDebug表示了当前页面中使用的dojo内容是否处在调试状态,在调试状态可以输出更多的内容,一般开发的时候我么选择true。parseOnLoad表示所有的dijit组件是否在html页面装载的时候自动的做展现(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>
上述的代码说明了我们需要在后续的代码中使用的dojo组件。
第三步,引用需要使用的CSS文件,由于dojo使用CSS实现了主题的概念,所以我们在使用dojo的时候一定要选择合适的主题。代码为:
<style type="text/css">
@import "/teamware/dojo-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">0.9.0</chsdate>beta/dojo/resources/dojo.css";
@import "/teamware/dojo-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">0.9.0</chsdate>beta/dijit/themes/dijit.css";
@import "/teamware/dojo-<chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">0.9.0</chsdate>beta/dijit/themes/tundra/tundra.css";
</style>
当然,也可以加入自己的内容,自定以主题。默认的主题时tundra主题。
另外需要注意,使用主题的时候需要在html的body元素上使用class属性,代码为:
<body class="tundra">
第四步(标签),使用dojo的dijit组件。
所有的组件都有一个dojo自定义的属性dojoType,dojoType决定了这个组件的dijit类型。例如:
日期组件
<input id="startDate" type="text" name="todo.startDate"
class="medium" value="<chsdate w:st="on" year="2005" month="12" day="30" islunardate="False" isrocdate="False">2005-12-30</chsdate>"
dojoType="dijit.form.DateTextbox"
constraints={min:'<chsdate w:st="on" year="2004" month="1" day="1" islunardate="False" isrocdate="False">2004-01-01</chsdate>',max:'<chsdate w:st="on" year="2006" month="12" day="31" islunardate="False" isrocdate="False">2006-12-31</chsdate>'}
required="true"
promptMessage="mm/dd/yyyy"
invalidMessage="Invalid date. Use mm/dd/yyyy format." />
这里dojoType=”dijit.form.DateTextbox”,表示这个组件的类型为DateTextbox类型,具体表现形式为:
<!--[if gte vml 1]><v:shape id="_x0000_i1028" type="#_x0000_t75" style='width:212.25pt;height:241.5pt'> <v:imagedata src="file:///C:\DOCUME~1\mazhao\LOCALS~1\Temp\msohtml1\01\clip_image007.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
时间组件:
<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." />
具体表现形式为:
<!--[if gte vml 1]><v:shape id="_x0000_i1029" type="#_x0000_t75" style='width:273.75pt;height:57pt'> <v:imagedata src="file:///C:\DOCUME~1\mazhao\LOCALS~1\Temp\msohtml1\01\clip_image009.png" o:title="" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->
第四步(JS编程动态实现),使用dojo的dijit组件。
在使用Struts2的过程中,为了方便操作,我们一般使用Struts2的内置标签,例如:<s:text/>等。但是使用这些标签有一个问题,这些标签都是Struts2的标签,在定制这些标签的时候没有考虑到dojo,所以这些标签中不能使用dojoType属性,所以要想在Struts2中要想使用dojo,就得:要么放弃这些好用的Struts2标签,要么,就是我们要讲的使用JS动态的使用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来动态的使用Dojo呢?
基本原理如下:
<!--[if !supportLists]-->(1) <!--[endif]-->构造组件需要使用的dojo属性
<!--[if !supportLists]-->(2) <!--[endif]-->找到需要套用dojo组件的HTML 元素,使用前面定义的属性建立新的替代组件,
例如,现在要将
<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的构造方法来创建dojo类型的组件。
但是需要注意一点,上述代码必须在HTML页面load的时候执行,否则无法正常创建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>