Dojo 日期控件的国际化和集成

来源:互联网 发布:mac更换输入法 编辑:程序博客网 时间:2024/04/18 08:25

黄 腾龙 (huangtl@cn.ibm.com), 软件工程师, IBM
赵 迎威 (yingweiz@cn.ibm.com), 软件工程师, IBM

2009 年 3 月 20 日

Dojo是一个开源的 Javascript 工具箱,Dojo widgets 可以提升您的 Web 应用程序的可用性、交互能力以及功能。本文介绍Dojo 日期控件的基础架构及扩展点,分析 Dojo 日期控件的国际化策略,最后给出 Dojo 日期控件在与后台系统做集成时的一些实践经验。

Dojo 控件介绍

Dojo 日期控件类型

Dojo 目前支持两种日期控件:


图 1. dijit.form.TimeTextBox
图 1. dijit.form.TimeTextBox

该类型控件为时间控件 , 图中显示格式为 :hh:mm:ss,该控件支持国际化 , 其格式会根据国家的不同而相应变化。


图 2. dijit.form.DateTextBox
图 2. dijit.form.DateTextBox

该类型控件为日期控件 , 图中显示格式为 :yyyy-MM-dd,该控件也支持国际化。

Dojo 日期控件的类层次结构


图 3. Dojo 日期控件类结构图
图 3. Dojo 日期控件类结构图

图 3 列出了 Dojo 日期控件所依赖的主要组件,下面分别介绍它们的功能:

  • dijit.form._ FormiWidget

    所有 dijit 控件的基类,定义了 Dojo 控件的一些共有特征,如 id,name 属性,onFocus,onChange 事件等。

  • dijit.form.TextBox

    普通 html 文本框的一个封装。

  • dijit.form.ValidationTextBox

    提供验证功能的文本框 , 子类可重写 isValid() 方法来自定义验证规则。

  • dijit.form.MappedTextBox

    提供了一个和文本框对应的隐藏域,用于存放 dojo 内部处理的结果 , 并提供了一个方法 serialize 来在内部值和显示值之间转化。

  • dijit.form.RangeBoundTextBox

    可定义取值范围的控件,并提供一个接口 isInRange 来判断是否在给定范围内。

Dojo 日期控件的初始化

Dojo 提供了两种初始化控件的途径:声明方式和编程方式。

声明方式

通过给 html 控件添加 dojoType 属性,其值为 dojo 控件的类名,在加载页面的过程中 Dojo 会将指定了 dojoType的控件转为 dojo 控件,如 <input type="text" dojoType="dijit.form.TimeTextBox"id="text001"> 。在页面加载完成后会转变为一个 dojo 时间控件。

Dojo 是如何把普通的html 控件转换为 dojo 控件的呢? dojo.parser 在其中起了主要的作用。 dojo.parser 会遍历页面取出有dojoType 属性的 html 元素,根据 dojoType 的值来初始化 dojo控件对象,同时会把页面中该元素的属性值做为参数传递给初始化方法,Dojo 把参数值转换为自己需要的类型。例如 :<inputtype="text" dojoType="dijit.form.TimeTextBox" id="text001"value="T14:22"> 在页面加载的时候 dojo 会初始化 dijit.form.TimeTextBox 类型的控件,同时会把value="T14:22" 做为参数传给初始化方法,但由于 dijit.form.TimeTextBox 对应的 value 属性的值是Date 类型,所以 dojo.parser 会对其进行转换。此时会用到 dojo 日期转换功能,在后面会详细介绍,代码如下 :


清单 1. 声明方式初始化 dojo


<script djConfig="parseOnLoad:true,isDebug:true,locale:'zh-cn'"src="<%=Context%>
/javascript/dojo/dojo.js" ></script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.TimeTextBox");
</script>
<body class="tundra">
<input type="text" id="text001" dojoType="dijit.form.TimeTextBox" value="T14:22">



图 4. Dojo 日期控件类结构图

编程方式

dojo 允许以更加面向对象的方式来使用 dojo 控件。对于上面的例子,可以采用如下方式来初始化。

new dijit.form.TimeTextBox( 
{"id":"text002","value":dojo.date.locale.parse(" 下午 2:22",{selector:"time"})},
dojo.byId("text001"));

 

该方法有两个参数 , 第一个为 dojo 对象属性值的一个集合,第二个参数指出了 dojo 控件在页面上的位置,在此例中 id 值为text001 的 html 元素将被替换为 dojo 控件(注:原来的 html 控件将被移除)。第一个参数中我们同样有两个属性 id 和value,由于编程方式定义的 dojo 控件不再经过 dojo.parser 处理,因此 value 属性的值必须是日期类型,我们用dojo.date.locale.parse 方法来将字符串转换为 Date 类型,此处我们会看到同样是把字符串转换为 Date类型,不同的方法数据格式会有很大不同,稍后会对此进行详细介绍 , 详细代码如下:列表 (lists) 是文章中常用的元素。列表分为有序列表 (带有数字顺序的列表 ) 和无序列表 ( 不带数字顺序的列表 ) 。


清单 2. 编程方式初始化 dojo


<script djConfig="parseOnLoad:true,isDebug:true,locale:'zh-cn'"src="<%=Context%>
/javascript/dojo/dojo.js" >
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.TimeTextBox");
</script>
<body class="tundra">
<input type="text" id="text001">
<script>
new dijit.form.TimeTextBox(
{"id":"text002","value":dojo.date.locale.parse
(" 下午 2:22",{selector:"time"})},dojo.byId("text001"));
</script>



图 5. 编程方式初始化 dojo 效果

我们可以看到,这两种方式所产生的效果是完全一样的。声明方式定义 dojo 控件实现起来比较简单,而且 dojo.parser 帮助我们做了很多辅助工作。编程方式稍微复杂些,但相比声明方式,编程方式更灵活,我们可以很方便的控制它的初始化过程。

两种方式结合

当然有些情况下会有这种需求 , 既想拥有编程方式的灵活性,又想拥有声明方式的简单性,比如我想自己来控制什么时候生成 dojo控件,又不想自己写方法来进行参数值转换,可通过如下方式来实现,在 html 代码中不添加 dojoType属性,在我们需要的时候,通过编程方式来指定 html 控件的 dojoType 属性值,然后通过调用dojo.parser.instantiate() 方法,来解析 html 代码生成 dojo 控件,这样我们既能动态的控制 dojo控件的生成,又能利用 dojo.parser 的强大功能,对于上面提到的例子我们可采用如下代码实现:


清单 3. 两种方式结合页面代码


<script djConfig="parseOnLoad:true,isDebug:true,locale:'zh-cn'"src="<%=Context%>
/javascript/dojo/dojo.js" >
</script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.form.TimeTextBox");
function createDojo(inputId){
var inputObj = document.getElementById(inputId);
inputObj.setAttribute("dojoType", "dijit.form.TimeTextBox");
dojo.parser.instantiate([inputObj]);
}
</script>
<body class="tundra">
<input type="text" id="text001" onfocus="createDojo('text001')">
</body>

 

其页面效果同前两种方式完全一样。

 




回页首

 

Dojo 日期控件的国际化策略

上一节对 Dojo 日期控件做了一个整体介绍,我们了解了 Dojo 的类层次结构以及如何使用 Dojo 。本节主要介绍 Dojo 日期控件对时间的处理以及对国际化的支持。

Dojo 日期控件对时间的处理

由于大部分 dojo 控件都继承自同一个基类,所以他们拥有相似的行为方式:

  • 都通过 onfocus 事件来触发 dojo 控件;
  • 如果继承自 dijit.form.ValidationTextBox,都通过 onkeyup 事件对值进行校验;
  • 都通过 onBlur 事件来关闭 dojo 控件等等。

日期控件同样具有上述行为,相应时间分别为:

  • Onfocus:打开日历窗口,并对当前值进行校验;
  • Onkeyup:对当前值进行校验;
  • onBlur:关闭日历窗口,并对当前值进行校验。

了解了 Dojo 日期控件的事件,下面将介绍我们 Dojo 提供的用于数据处理的方法 setValue() 和 getValue() 。

setValue 方法

在日期控件初始化时会给其传递一个 Date 类型的初始值,在初始化方法中正是通过调用 setValue() 来对其进行付值的setValue(Date):在该方法中会进行两个操作:用 format 属性定义的方法将 Date 类型数据转换为字符串,存到textbox 的 value 属性中,textbox 是我们可以在页面看到的 dojo 日期控件中的文本框;用 serialize属性定义方法将 Date 类型数据转换为字符串存到 valueNode 的 value 属性中,valueNode 既为 textbox所对应的隐藏域。 dijit.form.TimeTextBox 中默认的 format 方法为dojo.date.locale.format, serialize 方法为 dojo.date.stamp.toISOString 。

getValue 方法

getValue()方法返回的是日期控件的值类型为 Date 。上面提到 dojo 日期控件的两个域 textbox 和 valueNode中存储的都是字符串类型数据,因此该方法中会将其转换为 Date 类型 .dijit.form.TimeTextBox 默认使用的是dojo.date.locale.parse (/*String*/value, /*Object?*/options)方法该方法会根据指定的 locale( 在 djConfig 中指定 ) 和格式 ( 在输入参数 selector 属性中指定 ) 来将textbox 中的值转换为 Date 类型。

Dojo 日期控件对国际化的支持

Dojo 日期控件对国际化的支持主要集中在 dojo.date 包下的两个类 dojo.date.locale 和 dojo.date.stamp 。

dojo.date.stamp

基于 ISO-8601 标准,在 Date 和 String 类型之间进行转换。 ISO-8601 支持的格式如下:

日期
yyyy
yyyy-MM
yyyy-MM-dd
时间
THH:mm
THH:mm:ss
THH:mm:ss.SSS

 

dojo.date.stamp 类中包含的方法为:

dojo.date.stamp.fromISOString

用于把 String 转为 Date.

例 : dojo.date.stamp.fromISOString("2005-06-30T08:05:00");

转换后的 Date 类型在 Firefox 下用 Firebug 打印的结果如下 :


图 6. Dojo 实际存储的日期值

而 dojo.date.stamp.fromISOString("2005-06-30 T08:05:00");

因为不符合 ISO-8601 标准,将产生一个空的 Date 对象。

dojo.date.stamp.toISOString

用于把 Date 转为 String

例:dojo.date.stamp.toISOString(new Date())

上面提到过,如果采用声明方式生成 Dojo 日期控件,则 Dojo 会用 dojo.date.stamp.fromISOString方法来对其初始化,因此我们在给控件复值的时候必须遵循 ISO-8601 标准,否则将会生成一个空值 . 如何在初始化时,让 Dojo接受非标准格式的数据呢?我们知道,Dojo 日期控件用两个域来存放数据 :textbox 和valueNode,无论初始化时它如何对数据进行处理,最终的结果都是存在这两个域里,所以我们可以将我们的值直接复给这两个域,这样即使dojo.date.stamp.fromISOString() 方法转换失败,我们也能将值赋给 Dojo 。例如:


清单 4. 直接给 dojo 隐藏域赋值


<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dojo.date.stamp");
dojo.require("dojo.date.locale");
dojo.require("dijit.form.TimeTextBox");
function createDojo(inputId){
var obj = dojo.byId(inputId);
var value = obj.value;
obj.setAttribute("dojoType", "dijit.form.TimeTextBox");
dojo.parser.instantiate([obj]);
obj = dijit.byId(inputId);
obj.textbox.value = value;
obj.valueNode.value = value;
}
</script>

 

当然 Dojo 并不推荐我们这样做,毕竟它已经向我们提供了对其值进行操作的接口:setValue() 和 getValue()。但由于声明方式中我们没有机会接触到这两个方法,所以如果不采用强制复值的方法,我们只能通过编程方式来生成 Dojo。在编程方式中,我们直接传递一个 Date 对象给 Dojo 而非字符串,所以在传递之前,可以自己定义转换方式来将 String 转为Date 。


清单 5. 通过 dojo 提供的接口来赋值


<script type="text/javascript">
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dojo.date.stamp");
dojo.require("dojo.date.locale");
dojo.require("dijit.form.TimeTextBox");
</script>
<body class="tundra">
<input type="text" id="text001">
<script>
new dijit.form.TimeTextBox({"id":"text002","value":dojo.date.locale.parse
(" 下午 2:22",{selector:"time"})},dojo.byId("text001"));
</script>
</body>

 

此处用到了 dojo.date.locale.parse 方法,下面会详细介绍。

Dojo.date.locale

基于指定的 locale,在 String 和 Date 之间进行转换。

主要介绍两个方法:dojo.date.locale.parse() 和 dojo.date.locale.format() 。

dojo.date.locale.parse()

用于把字符串按照指定参数转换为日期类型

其中字符串的格式必须和当前页面的 locale 相匹配,否则转换将失败 , 例 :


清单 6. 字符串转换为日期类型代码 1


<script djConfig="parseOnLoad:true,isDebug:true,locale:'zh-cn'" src="<%=Context%>
/javascript/dojo/dojo.js" >
</script>
<script>
dojo.date.locale.parse("12:30",{selector: "time"});
</script>

 

执行的结果为空,说明转换失败。 因为 locale: zh-cn 对应的时间格式为“下午 12:30 ”如换成如下代码 :


清单 7. 字符串转换为日期类型代码 2


<script djConfig="parseOnLoad: true, isDebug: true, locale:'zh-cn'" src="<%=Context%>
/javascript/dojo/dojo.js" >
</script>
<script>
dojo.date.locale.parse(" 下午 12:30",{selector: "time"});
</script>

 

则能转换成功。由此可以看到,如果要将字符串转为日期类型,字符串的格式必须和当前页面的 locale 相匹配,这就对转换增加了一些约束。

dojo.date.locale.format()

用于把日期类型按照指定的格式转换为字符串。

在将日期类型转换为字符串时,并不要求必须与 locale 相匹配,因此使用起来更灵活些,例


清单 8. 日期转换为字符串代码 1


dojo.date.locale.format(new Date(), {selector:'time', timePattern:'HH:mm:ss'});

 

转换后结果为 : 10:18:41


清单 9. 日期转换为字符串代码 2


dojo.date.locale.format(new Date(), {selector:'date', datePattern:'yyyy-MM-dd'});

 

转换后结果为 : 2008-10-10


清单 10. 日期转换为字符串代码 3


dojo.date.locale.format(
d,{selector:'datetime', datePattern:'yyyyMMdd',timePattern:'HHmmss'});

 

转换后结果为 : 20081010 101841

上述方法在 Dojo 日期控件中的应用

上面介绍了 dojo 提供的用于时间处理的一些工具,下面将介绍 dojo 日期控件是如何使用这些工具的。在给 dojo 日期控件负值的时候,会用format 属性定义的方法将其转化为字符串存到 textbox, 做为显示值用 serialize 属性定义方法将 Date类型数据转换为字符串 存到 valueNode,做为 dojo 日期控件的实际值在从 dojo 日期控件取值的时候,用 parse属性定义的方法,将 textbox 中的值转换为 Date 类型。

  • format 属性对应的方法为 dojo.date.locale.format, ;
  • serializet 属性对应的方法为 dojo.date.stamp.toISOString ;
  • parse 属性对应的方法为 dojo.date.locale.parse ;

format 方法 :dojo.date.locale.format

会根据指定的格式对 dateObject 进行转换。

我们可以通过在页面中如下配置来指定 locale: <script djConfig="parseOnLoad: true,isDebug: true, locale:'zh-cn'"> 对于日期格式可在输入参数 options 的 selector属性中定义。目前 dojo 支持三种格式 :date,time,timestamp 。但 dojo 只对前两种定义了控件类dijit.form.DateTextBox 和 dijit.form.TimeTextBox,如果我们需要 timestamp类型的控件可通过扩展 dijit.form.TimeTextBox 来实现,代码如下:


清单 11. 扩展 dojo 日期控件


dojo.declare("dijit.form.TimestampTextBox",dijit.form.TimeTextBox,{
postMixInProperties: function(){
this.inherited('postMixInProperties', arguments);
this.constraints.selector = 'timestamp';
}
});

 

当然也可以自定义 format 方法来对 Date 类型进行转换 .

serialize 方法 : dojo.date.stamp.toISOString

按照 ISO-8601 标准来对 Date 类型进行转换,转换后的值被存在 valueNode 里,而此值是 dojo日期控件的真实值,所以如果其格式不能满足我们的要求时,可通过自定义 serialize 方法来解决 , 例:中国时间上午 10:17在页面中显示为:上午 10:17,如果用默认的 serialize 方法则其真实值为 :T10:17:00, 我们看到时间前多了个 T,如果我们想最终结果为 10:17:00, 则可如下定义 serialize 方法 .


清单 12. 自定义 serialize 方法


function(d, options) {
return dojo.date.locale.format(
d, {selector:'time',timePattern:'HH:mm:ss'}
);
}

 

在上面已经详细介绍了这几个方法,如果在实际应用中,上述方法不能满足要求时,我们可以通过更改相关属性对应的方法来达到目的。同时,我们注意到,上述方法中都有一个 options 参数,该参数是用来指定转换的格式的,它是一个 Object 类型的数据,对应以下属性:selector:用来指定要转换的类型,dojo 已定义的值有 date( 只转换日期部分 ) 和 time ( 只转换时间部分 ), 如果为其他值则将会把date 和 time 都包括进来

formatLength: 转换后字符串的长度 可选值有 long, short, medium , full 默认是 short

datePattern: 日期部分的格式

timePattern:时间部分的格式

locale:设定国际化参数

 




回页首

 

与后台系统的集成

通过以上对 Dojo 日期控件国际化支持的介绍,我们知道 Dojo 作为一种流行的 Javascript开源框架,其对日期国际化的支持非常强大。目前的很多 Web 应用都是基于 Dojo来实现日期的国际化。但不是每个后台的系统对日期国际化的支持都是那么强大,有些后台系统可能对日期国际化的支持比较有限,这时如果需要将 Dojo和这些后台系统进行集成来支持日期国际化的时候,就需要我们做一些额外的开发工作。这里我们以 IBM 的 DB2 Content Manager来举例说明 Dojo 如果和跟后台系统进行集成,同时我们也给出一些集成方面的开发技巧。

IBM DB2 Content Manager 对日期国际化的支持:

DB2Content Manager 是建立在 DB2 Universal Database™ 存储过程基础上的内容储存库,利用了 IBMWebSphere Application Server 提供的内容检索和安全性以及 Tivoli® Storage Manager提供的对象迁移及备份与恢复。 CM 可作为结构化和非结构化数据的企业储存库用来存储任何格式的文档。 DB2 Content Manager支持内容国际化 (Globalization),但是对日期类型的国际化支持的并不是很好。比如,对于 Date 类型的数据,CM要求其格式必须是 yyyy-MM-dd 。 如果某一个国家默认的日期格式是 MM-dd-yyyy,这种格式的日期数据是没法保持到 CM中的,唯一的办法是将其它日期的格式转成 yyyy-MM-dd 格式,然后再进行保存。下面列出 CM 支持的日期格式:


清单 13. CM 支持的日期格式

TypeFormatDateyyyy-MM-ddTimeHH.mm.ss 或 HH:mm:ssTimestampyyyy-MM-dd-HH.mm.ss.SSSSSS
或 yyyy-MM-dd HH:mm:ss.SSSSSS

 

通过以上对 CM 支持的日期格式的分析,我们可以得出结论:对于要保存到 CM 的日期数据需要先进行格式转化,然后再保存。在使用 Dojo作为前台,CM 作为后台的 Web 应用程序开发中,Dojo 在前台 Web 页面对日期国际化的支持非常强大,因此需要在 Dojo日期控件把日期传到后台 CM 时先把日期转成 CM 能够支持的格式,流程图如下:


图 7. Dojo 和 CM 日期转换流程图
图 7. Dojo 和 CM 日期转换流程图

通过上面的流程图我们可以清楚的知道,在 Dojo 日期控件向 CM 传送日期数据之前需要对日期进行格式转换,下面针对 Date、Time、Timestamp 三种类型的数据分别介绍如何进行日期转换:

Date


清单 14. Date 类型转换代码

dojo.declare("MyDateTextBox",[dijit.form.DateTextBox],{ 
serialize: function(d, options) {
return dojo.date.locale.format(
d, {selector:'date',
datePattern:'yyyy-MM-dd',
timePattern:'HH:mm:ss'}).toLowerCase();
},
});

 

说明:定义自己的 dojo Date 控件,需要继承 dijit.form.DateTextBox (Dojo 中默认的 Date 控件 ) ;

重载 dijit.form.DateTextBox 中的 serialize 方法:指定当前控件的类型为 date (selector: ’ date ’),将控件中的 Date 值转成 yyyy-MM-dd 格式。

Time


清单 15. Time 类型转换代码

dojo.declare("MyTimeTextBox",[dijit.form.TimeTextBox], { 
serialize: function(d, options) {
return dojo.date.locale.format(
d, {selector:'time',
datePattern:'yyyy-MM-dd',
timePattern:'HH:mm:ss'}).toLowerCase();
},
});

 

说明:定义自己的 dojo Time 控件,需要继承 dijit.form.TimeTextBox (Dojo 中默认的 Time 控件 );重载 dijit.form.TimeTextBox 中的 serialize 方法 : 指定当前控件的类型为 time (selector:’ time ’), 将控件中的 Time 值转成 HH:mm:ss 格式。

Timestamp


清单 16. Timestamp 类型转换代码


dojo.declare("MyTimestampTextBox",[dijit.form.TimeTextBox], {
serialize: function(d, options) {
return dojo.date.locale.format(
d, {selector:'datetime',
datePattern:'yyyy-MM-dd',
timePattern:'HH:mm:ss'}).toLowerCase() + ".000000";
}
});

 

说明:定义自己的 dojo Timestamp 控件,需要继承 dijit.form.TimeTextBox (Dojo 中默认的 Time控件 ); 重载 dijit.form.TimeTextBox 中的 serialize 方法 : 指定当前控件的类型为 datetime(selector: ’ datetime ’), 将控件中的 Timestamp 值转成 yyyy-MM-ddHH:mm:ss.SSSSSS 格式 , 本例中将最后的六位微秒统一置为 0, 因为在平时的应用中基本不会使用到后面的微秒,当然也可以根据实际的需要置成不同的值。

 




回页首

 

总结

Dojo是一个功能丰富,易于扩展的 JavaScript 工具包,同时提供了面向对象的编程模式。本文主要介绍了 dijit包下的日期控件,对其框架结构,使用方式,扩展点都作了详细介绍。通过本文的学习相信可以使您在项目中熟练的使用 dojo日期控件,同时在学习使用其他 dojo 控件时,也为您提供了很好的学习思路。

 

参考资料

学习

  • Dojo 的 Wiki:对 dojo 进行了全面的介绍,是一个入门的好地方。
  • Dojo 官方网站:提供了最全面的资料,包括 dojo 源码,各种文档,实例等等。
  • Dojo book:很全面的官方教程,是学习 dojo 不可不读的资料,介绍得很全面。
  • 基于 Dojo 的本地化开发:developerWork 上的一篇不错的讲解 dojo 本地化的文章。
  • developerWorks Dojo 专题:汇集了大量与 Dojo 相关的技术资源。
  • 通过 developerWorks Web 开发专区 内有关 Web 技术的文章和教程(包括本专栏之前的各期文章)扩展您的站点开发技能。


获得产品和技术

  • IBM 试用软件:使用这些可直接从 developerWorks 下载的软件构建您的下一个开发项目。


讨论

  • 通过参与 developerWorks blog 加入 developerWorks 社区。

 

作者简介

 

黄腾龙,软件工程师,目前在 IBM 中国软件开发中心工作。对 J2EE 应用程序开发,软件过程及自动化测试有着浓厚的兴趣。

 

 

赵迎威,现在 IBM 中国软件开发中心工作,目前从事 web solution 方面的开发工作。

原创粉丝点击