Dojo 是一个开源的基于 JavaScript 的 Web 界面开发框架,它对常用对象进行了包装并提供一系列 widgets 来简化用户界面的开发。Dojo 的一个重要特性就是提供了良好的本地化支持机制。包括时间/日期格式的本地化和使用不同的日历。Dojo 提供了一系列的支持本地化的输入型 Widgets,其中包括:ValidationTextBox,CurrencyTextBox,NumberTextBox,DateTextBox,TimeTextBox,这些 TextBox 本身就可以自动完成货币,数字,日期时间等的格式的本地化,但是在实际项目中会遇到一些新的问题,以及需要自定义一些 widget。
DateTextBox 就是一个允许用户输入和日历选择的易于使用的日期小控件。DateTextBox 控件有一些自己的属性可以设置,表 1 和表 2 列出了 DateTextBox 常用的一些属性和方法的介绍及实例。
属性 | 描述 | 例子 |
---|---|---|
constraints | 用户自定义的参数集合 | this.DateTextBox.set("constraints", { selector: "date", datePattern: "dd/MM/y", locale: "fr-fr" }); |
lang | 设置用户语言环境,覆盖默认的 dojo locale | this.DateTextBox.set("lang", dojo.config.culturalLocale); // dojo.config.culturalLocale 是获取到的浏览器 locale |
required | 是否允许为空,true:不允许为空;false:允许为空 | This.DateTextBox.set("required",true); |
value | 对应 HTML<input>元素的属性 | This.DateTextBox.set("value", new Date()); |
displayedValue | 实时显示出来的值 | this. DateTextBox.get("displayedValue") |
domNode | DateTextBox 规范的顶级节点 | dojo.addClass(this.DateTextBox.domNode, "ls-inputError"); |
方法 | 描述 | 例子 |
---|---|---|
postMixInProperties() | 在控件的 DOM 对象创建前对 Widget 的变量进行处理,可以在子类中覆写这个方法; | postMixInProperties: function () { this.valueLabel = dojo.i18n.getLocalization(""g11n.messages"", " CommonResources", "zh-CN"); |
postCreate() | 覆写初始化函数,大部分可以初始化操作可以在这处理,如绑定事件和设置 CSS 属性。 | postCreate: function(){ this.DateTextBox.set("constraints", 'dd-MM-yyyy'); this. DateTextBox.set("lang","zh-CN"); } |
_getValueAttr() | 从文本框中获取日期信息 | _getValueAttr: function() { return this. DateTextBox.get("value"); } |
_setValueAttr(value, priorityChange, formattedValue) | 设置这个文本框的日期。设置的值可以是 JavaScript 日期或要解析的字符串。 | _getValueAttr:function(value){ this. DateTextBox.set("value", dojo.date.locale.parse(value, {selector: "date", datePattern: formattedValue, locale: "en-us")) } |
loadAndOpenDropDown() | 使得 DateTextBox 获得鼠标焦点 | popupCalendar: function() { this.dateTextBox.focus(); this.dateTextBox.loadAndOpenDropDown(); } |
displayMessage(message) | 这是一个重写的方法,用来显示验证错误和提示信息,默认情况下使用 tooltip 显示提示信息 | this.DateTextBox.displayMessage = dojo.hitch(this, function(message) |
日期通常被当做文本字符串处理,这样就有不同的表达方式,比如 5/8/2016,在英国指 2016 年 8 月 5 日,而美国指 2016 年 5 月 8 日。Dojo 对本地化有很好的支持,对于不同国家的用户会自动完成日期的解析。
dojo.date.locale 就是被用来处理根据用户自定义的 locale 作为用户默认的语言环境,如果用户不明确指定,dojo 会根据浏览器的 locale 对这个属性赋值。和 Java 不同,目前在 dojo 中 locale 并没有对应对象,只是一个 String 对象,典型的格式应该是"zh-CN"。注意 dojo 用的是 "-" , 而不是 Java 中的 "_"。例如对于 5/8/2016,当获取到的 locale 是 en-US 的时候,就被解析为 2016 年 5 月 8 日;当获取到的 locale 是 en-UK 的时候,就被解析为 2016 年 8 月 5 日。
另外一个问题就是我们的应用程序可能会和不同地区的各类用户进行交互,但是往往只有一个服务器端,比如我们的控件上标识的属性值为 5/8/2016,对于字符串 5/8/2016,DateTextBox 如何知道这个日期代表的含义呢?为了避免歧义,当与 JavaScript 域之外进行通信时,DateTextBox 采用 ISO8601/RFC3339 的格式 yyyy- MM-dd 或者 yyyy-MM 或者 yyyy 来描述日期,这种格式对于文化习惯引起的格式惯例和时区都无偏向性。例如:2007-12-25 表示 2007 年 12 月 25 日。ISO 格式的日期值能够正确的按照字符串排序 ,而且比 JavaScript 的日期对象要轻量的多,因此使得其更方便用于编程。
理想情况下,服务器端的日期格式遵循 ISO 标准,但是实际上服务器端的数据往往并不是 ISO 标准日期。比如我们的后台数据库是 Oracle 数据库的时候,默认情况下 Oracle 用 dd-MM-yyyy 格式(ISO 格式 yyyy-MM-dd)。这种情况可以通过创建自定义 widget 实现接受来自服务器端的日期格式,并且在客户端自动转换为用户所在国家文化习惯的日期格式。这个功能主要通过 DateTextBox 的 get("value")和 set("value",value) 来实现的。
DateTextBox.get("value"):返回一个 javaScript Date 对象,可以利用 dojo.locale.format() 方法将取到的日期类型按照指定的格式转换成为字符串返回给服务器端。
DateTextBox.set("value",value):接受一个 javaScript Date 对象,然后利用 dojo.locale.parse() 方法将来自服务器端的带日期的字符串格式化成为当前用户所在国家的日期类型。清单 1 是使用实例:
serverFormat: { selector: 'date', datePattern: 'dd-MM-yyyy', //服务器端是 oracle 日期格式 locale: 'en-us' }, valueToSendToServer = dojo.date.locale.format(this.dateTextBox.get("value"),serverFormat); valueToDisplayToClient = this.dateTextBox.set("value", dojo.date.locale.parse(valueFromServer, serverFormat));
清单 2 是一个 DateTextBox 服务器端发送/接收非 ISO 标准日期格式的例子
require(["dojo/_base/declare", "dijit/form/DateTextBox", "dojo/date/locale", "dojo/dom", "dojo/domReady!"], function(declare, DateTextBox, locale, dom){ declare("OracleDateTextBox", DateTextBox, { oracleFormat: { selector: 'date', datePattern: 'dd-MMM-yyyy', locale: 'en-us' }, value: "", postMixInProperties: function(){ // 将字符串类型转换为日期类型的对象 this.inherited(arguments); // convert value to Date object this.value = locale.parse(this.value, this.oracleFormat); }, // 覆写 serialize 方法,将日期类型的对象转换成字符串类型写会到服务器端: serialize: function(dateObject, options){ return locale.format(dateObject, this.oracleFormat).toUpperCase(); } }); //从服务器端获取日期并且显示出来 function showServerValue(){ dom.byId('toServerValue').value = document.getElementsByName('oracle')[0].value; } new OracleDateTextBox({ value: "21-JUN-2016", name: "oracle", onChange: function(v){ setTimeout(showServerValue, 0)} }, "oracle").startup(); showServerValue(); });
当我们想以编程方式打开日历,例如鼠标点击日历图标的时候,弹出日历选择控件,当鼠标焦点离开日历图标的时候,自动关闭日历。另外当用户输入格式不正确的时候,提供一些提示和警告信息,这时候我们就需要定制自己的 DateTextBox 控件。下面详细介绍了如何创建一个定制的可复用的 DateTextBox 小控件。
步骤 1: 创建一个 css 样式文件,如 myDateTextBox.css
清单 3 是 css 文件的一个片段,定义 Calendar 图标的样式。
.calendarIcon { display: block; width: 24px; height: 22px; background: url("/images/calendar-icon.png") no-repeat 0 0 transparent; cursor: pointer; margin-left: 5px; } .calendarIcon:hover { background-position: 10 0; }
步骤 2: 创建模板文件 myDateTextBox.html
使用 dojoAttachPoint 属性定义 DOM 对象的唯一 ID,如清单 4 所示的实例。
<div class="myDateTextBox"> <div dojoAttachPoint="dateTextField" dojoType="dijit/form/DateTextBox"></div> <div dojoAttachEvent="onclick:popupCalendar" class="calendarIcon"></div> <div dojoAttachPoint="inputFormat" class="inputFormat"></div> </div>
步骤 3:创建 js 文件,MyDateTextBox.js
通过使用 declare 可以很容易创建我们自己定制的 Dojo 小控件,创建步骤包括:
第一步:引用 Dojo 工具包和定义类 ,继承 dijit._Contained,dijit._Widget,dijit._Templated 和 dijit.form.DateTextBox,如清单 5 所示。
define([ "dojo/_base/declare", "dojo/_base/config", "dojo/_base/lang", "dojo/date/locale", "dojo/dom-class", "dijit/_Contained", "dijit/_WidgetBase", "dijit/_TemplateMixin", "dojo/text!./templates/ MyDateTextBox.html" ],function(declare, config, lang, locale, domClass, contained, widgetBase, templateMixin,template){
return declare("common.MyDateTextBox", [contained, widgetBase, templateMixin],{
第二步: js 文件的主体部分。
包括定义成员变量及初始化,定义初始化函数,覆写构造函数和初始化函数 postCreate,自定义函数等。如清单 6 所示。
define([ "dojo/_base/declare", "dojo/_base/config", "dojo/_base/lang", "dojo/date/locale", "dojo/dom-class", "dijit/_Contained", "dijit/_WidgetBase", "dijit/_TemplateMixin", "dojo/text!./templates/ MyDateTextBox.html" ],function(declare, config, lang, locale, domClass, contained, widgetBase, templateMixin,template){ return declare("common.MyDateTextBox", [contained, widgetBase, templateMixin],{ templatePath: template, dateTextField: null, // DateTextBox widget serverDatePattern: 'dd-MM-yyyy', errorNode: null, serverFormat: { selector: 'date', datePattern: 'dd-MM-yyyy', //服务器端是 oracle 日期格式 locale: 'en-us' }, postCreate: function() { this.dateTextField.set("constraints", { selector: "date", formatLength: "short") }; this.dateTextField.set("lang", config.locale); this.dateTextField.set("required", true); this.dateTextField.displayMessage = lang.hitch(this, function(message) { if (message) { this.setError(); } else { this.removeError(); } }); }, popupCalendar: function() { this.dateTextField.focus(); this.dateTextField.loadAndOpenDropDown(); }, setValue: function(value) { this.dateTextField.set("value", locale.parse(value, this.serverFormat)) }, getValue: function() { return locale.format(this.dateTextField.get("value"), this.serverFormat }, validate: function () { this.removeError(); if (!this.dateTextField.isValid()) { this.setError(); return false; } else { return true; } }, setError: function(serverErrorMessageIgnored) { this.errorNode.innerHTML = this._datePattern(); domClass.removeClass(this.errorNode, "hidden"); domClass.addClass(this.dateTextField.domNode, "ls-inputError"); }, removeError: function() { this.errorNode.innerHTML = ""; domClass.addClass(this.errorNode, "hidden"); domClass.removeClass(this.dateTextField.domNode, "ls-inputError"); }, });
第三步:在 HTML 或者 JSP 中使用自定义日期控件,如清单 7 所示:
<td> <div dojoAttachPoint="dateTextField" dojoType="dijit/form/DateTextBox"></div> </td> <td> <div dojoAttachPoint="calendarButton" dojoAttachEvent="onclick:popupCalendar" class="calendarIcon"></div> </td>
1. dojo.date.locale 使用横线 '-'作为 locale 的分隔符,Java 使用下划线'_'作为分隔符。
2. Dojo.locale.parse,Dojo.locale.format 和 Dojo.digit.DateTextBox 不遵循 icu4j 规范。无论哪个区域,Dojo 都是以四位数表示日期,如果强制使用两位数表示日期,将 constraints 中的 fullYear (boolean) 设置为 false。
本文介绍了 dojo DateTextBox 控件的基本属性,方法,客户端和服务器端如何解析区域自适应的日期格式,以及如何创建自定义的日期控件。希望这篇文章能为正在开发国际化 Web 应用程序的读者提供一定的参考价值。