用 Dojo 1.x 开发小部件
学习使用 Dojo JavaScript 工具箱开发 HTML 小部件的基础知识。本文为您提供了一个简介,此外,还给出了几个例子为开发过程提供帮助 — 以简单的示例小部件开始,随后是复杂一些的小部件,同时还突出介绍了在开发过程中可能遇到的一些常见问题。
简介本文的目标是为您提供使用 Dojo JavaScript 工具箱开发 HTML 小部件的基础知识,首先从版本 1.0 开始。本文还描述了几个示例,以简单的小部件开始,之后是较为复杂的小部件,同时还会解决在小部件开发过程中经常遇到的一些常见问题。 回页首 何为 Dojo 工具箱?Dojo 是一种基于 JavaScript 的开源工具箱,可用来开发动态 HTML Web 应用程序。借助它,可以快速构建较标准 HTML 小部件更为复杂的小部件。使用 Dojo 提供的组件可以让 Web 用户界面的可用性、响应性和功能性都有所提高。由 Dojo 提供的低层 API 和兼容性层可帮助您编写跨浏览器兼容的应用程序。 回页首
开始之前在开始之前,需要设置开发环境。为此:
完成之后,文件夹的结构应该类似图 1。 图 1. dojo 文件展开后的文件夹结构Dijit是位于 dojo 之上的一个小部件系统。通过自身的主题tundra,它为其所有的小部件提供了通用的设计和颜色模式。Dojox是一个开发包,由 Dojo 工具箱的扩展组成。它可用于开发常见集合中所没有的那些功能。 回页首
Dojo 小部件在浏览网站时,会看到数百个小部件呈现在屏幕前。Web 浏览器的每个按钮都是一个小部件。每个文本输入框也是一个小部件。标准的 HTML 提供了小部件的有限集合:一个输入框、一个按钮和一个超级链接。 Dojo 小部件接受像文本输入框这样的条目并会添加一些函数来获得更具用户友好性的对象,比如一个便于选择日期的图形日历。并且在这个过程中,不会破坏新功能所基于的那个原始条目。 一个 Dojo 小部件封装了一些可视 Web 组件以便于重用。它由三个文件定义:
导入 Dojo 工具箱清单 1 显示了可用来将小部件导入到一个常规 Web 页面的基本 HTML 骨架。 清单 1. 将小部件导入到 Web 页面所需的 HTML 代码<html> <head> <title>Dojo Toolkit Test Page</title> <style type="text/css"> /* CSS style code */ </style> <script type="text/javascript" src="js/dojo1.2/dojo/dojo.js" djConfig="parSEOnLoad:true,isDebug:true"></script> <script type="text/javascript"> /* Javascript code */ </script> </head> <body> /* Widgets definition code */ </body> </html> 第一个脚本标记通过加载 dojo.js bootstrap 文件来初始化 Dojo 工具箱。djConfig对象的 清单 2. 用 djConfig 设置全局变量所需代码<script type="text/javascript"> var djConfig = { isDebug:true,parSEOnLoad:true }; </script> <script type="text/javascript" src="js/dojo1.2/dojo/dojo.js"></script> Dojo 包系统 Dojo 是一个包系统,可用来在文件内构造应用程序类并通过 为了创建一个小部件,必须通过添加清单 3 所示的代码行来导入这个小部件声明。 清单 3. 导入小部件声明所需代码<script type="text/javascript"> dojo.require("widgets.Button"); </script> 现在,将如下代码插入主体部分: <body> <div dojoType="widgets.Button">My Button</div> </body> 声明一个小部件 现在,让我们来看看这个 首先,必须创建这个 JavaScript 文件 TextBox.js,内含此小部件的定义和逻辑(参见清单 4)。 清单 4. JavaScript 文件 TextBox.js 的内容dojo.provide("widgets.TextBox"); dojo.require("dijit._Widget"); dojo.require("dijit._Templated"); dojo.declare( "widgets.TextBox",[dijit._Widget,dijit._Templated],{ /** the template path */ templatePath: dojo.moduleUrl("widgets","templates/TextBox.html"),/** the input DOM node */ inputNode: null,/** the label */ label: "",/** onkeyup handler */ onKeyUp: function() { // give a chance to the browser to update the DOM setTimeout(dojo.hitch(this,this.validate),0); },/** validate function */ validate: function() { if ( this.inputNode.value === "Ok" ) { // the text is correct dojo.addClass(this.inputNode,"inputOk"); dojo.removeClass(this.inputNode,"inputError"); } else { // the text is incorrect dojo.removeClass(this.inputNode,"inputOk"); dojo.addClass(this.inputNode,"inputError"); } } } ); dojo.provide()定义新的小部件的名称并注册此类声明。注意到:
现在,我们可以定义模板文件 TextBox.html,如清单 5 所示。 清单 5. TextBox.html 的内容<span class="textBox"> ${label}: <input type="text" class="inputOk" dojoAttachPoint="inputNode" dojoAttachEvent="onkeyup: onKeyUp"> </input> </span> ${label}将由此小部件实例的 label 属性替代。 dojoAttachPoint声明会导致 inputNode 小部件的属性被设置为这个输入标记所对应的那个 DOM 节点。 dojoAttachEvent声明会导致 类 清单 6. TextBox.css 内的 CSS 类名.ibm .textBox { margin: 5px; padding: 5px; background-color: #eee; } .ibm .inputOk { border: 1px solid green; } .ibm .inputError { border: 1px solid red; } 由于类名必须在整个项目内惟一,通常它们都具有一个 CSS 选择器(在本例中为ibm)。 最后,这个小部件可以在 HTML 页面上创建,如清单 7 所示。 清单 7. 在 HTML 页面上创建小部件所需代码<html> <head> <!-- page title --> <title>TextBox Widget</title> <!-- include the DOJO --> <script type="text/javascript" src="../dojo-1.0.0/dojo/dojo.js" djConfig="isDebug: true,parSEOnLoad: true"> </script> <!-- import DOJO base CSS,DIJIT theme,and widget CSS --> <style type="text/css"> @import "../dojo-1.0.0/dojo/resources/dojo.css"; @import "../dojo-1.0.0/dijit/themes/tundra/tundra.css"; @import "templates/TextBox.css"; </style> <!-- import DOJO stuff --> <script type="text/javascript"> dojo.require("dojo.parser"); <!-- register our module path --> dojo.registerModulePath("widgets","../../widget"); <!-- import our stuff --> dojo.require("widgets.TextBox"); </script> </head> <body class="tundra ibm" style="padding:5px"> <br/> <!-- declare the DOJO widget --> <div dojoType="widgets.TextBox" label="Name"> </div> </body> </html> 图 2 显示了这个 图 2. TextBox 小部件声明式和编程式Dojo 支持两种编程模型:声明式和编程式。如果需要,两个模型均可用在同一个页面上。在清单 7 中,小部件由一个声明式模型创建。 <div dojoType="widgets.TextBox" label="Name"></div> 同样地,这个小部件也可以用编程式模型创建,如清单 8 所示。 清单 8. 用编程式模型创建 TextBox 小部件回页首
有关 Dojo 小部件的更多内容 如之前所述,一个 Dojo 小部件就是一个继承自某个特定类(
小部件创建阶段 如果没有 ID,就为这个小部件创建一个惟一 ID。 所有 Dojo 小部件都必须由惟一 ID 标识。在小部件被初始化时,就可以提供小部件 ID。如果不提供,Dojo 就会创建一个具有如下格式的 ID: packageName_className_number 例如,一个按扭可以有这样一个自动生成的 ID:dijit_form_Button_0。 packageName是小部件包,点被换成了下划线(例如,dijit_form),而number是一个在小部件的基础上不断变大的数字。 在全局小部件注册表中注册这个小部件 随后,可以用 映射属性 小部件属性可以被重新映射到小部件模板的用户定义节点。要映射的属性及映射的目标可在
清单 9 给出了一个示例 清单 9. 声明一个 dojo 小部件dojo.declare( "MyWidget",dijit._Widget,{ // the attribute to map (name and value) title: "myTitle",// the DOM node to be used to set the // title attribute to the "myTitle" value titleNode: null,// the attribute map // the title attribute is mapped to the titleNode attributeMap: {title: "titleNode"},// the template string templateString: "<div><span dojoAttachPoint="titleNode"></span></div>", 得到的小部件模板将是: <div><span title="myTitle"></span></div> 请注意,由于 清单 10. 混合基础类 attributeMap 属性与定制值attributeMap: dojo.mixin( dojo.clone(dijit._Widget.prototype.attributeMap),{id:"focusNode",tabIndex:"focusNode",alt:"focusNode"} ), 模板方法在小部件创建阶段,我们要在某些特定的初始化阶段调用一些方法来为实现者提供一个执行一些操作的机会。下面的方法会被调用:
模板方法可以由实现者提供,但必须注意的一点是超类有可能已经实现了其模板方法。要确保对链中所有类的正确初始化,实现者必须手动编写超类方法调用的代码,如清单 11 所示。 清单 11. 超类方法调用的示例startup: function() { // call the superclass' method this.inherited("startup",arguments); // your code here } 作为一个通用约定,对于所有创建方法(除 uninitialize方法则要遵照相反的规则。 最后一个要注意的是 var w = new MyWidget({}); w.startup(); 小部件销毁阶段创建后,小部件将一直存在,直到执行一个显式的销毁请求。实现者负责管理小部件的整个生命周期,包括小部件的销毁。否则,将会导致有遗漏的小部件存在,直到整个页面清理发生。这个小部件提供了四种销毁方法:
在本示例中,小部件包含有内部小部件(例如:在其模板中有一些小部件),所以实现者必须要记得触发子小部件,以便销毁这些子小部件并且无需让小部件用户决定调用何种销毁方法。清单 12 给出了实现此目的一种可能方式。 清单 12. 触发小部件销毁的示例uninitialize: function() { // destroy the descendants this.destroyDescendants(); // call the superclass' method this.inherited("uninitialize",arguments); } 事件管理 Dojo 小部件可以对来自于 DOM 节点或对象的外部事件做出反应。这类事件可通过使用小部件的 connect: function(/*Object|null*/ obj,/*String*/ event,/*String|Function*/ method); 或者,也可以通过使用 如果,在小部件生命周期内一些连接已经不再需要,那么实现者就可以通过调用 小部件模板 如前面看到的,小部件必须继承自 Dojo 提供了一个强大的抽象,即 清单 13. 从 _Templated 类继承dojo.declare( "widgets.MyWidget",dijit._Templated { templatePath: dojo.moduleUrl("widgets","templates/MyWidget.html"),} ); _Templated类提供了它自身的
第一种选择是一种组织小部件源代码的最干净的方法,因为这个标记可被写入一个不同的文件,也可被以一种可读的方式格式化,同时不必费心于字符串的串联,并且字符串分界符不可转义。而第二种选择要更好一些,因为无需从浏览器载入第二个文件。然而,您不必担心,因为 Dojo 提供了一个构建工具,能将外部模板内部化到小部件源代码。 一个模板可以通过引用小部件属性而被参数化。在小部件公开某些能直接影响此标记的配置参数时,或是这个标记必须要依照外部首选项而有条件地生成时,这一点将会很有用。小部件属性通过使用这样的语法引用: 模板可以包含其他的小部件声明。不过,为了将它们考虑进去,小部件开发人员必须将 模板可以包含如下两种特殊的属性声明:
清单 14 给出了一个模板示例。 清单 14. 特殊属性声明的模板示例<span class="textInputDefault" dojoAttachEvent="onclick:_onClick"> <img class="textInputIcon" src="${constants.DEFAULT_ICON}" dojoAttachPoint="_iconNode"/> <input class="textInputNode" size="${size}" type="${type}" value="" dojoAttachPoint="_inputNode,_focusNode" dojoAttachEvent="onkeyup:_onKeyUp,onkeypress:_onKeyPress,onchange:_onChange" /> <span class="textInputRight"> </span> </span> 回页首
Tivoli Dynamic Workload Console 与 DojoTivoli Dynamic Workload Console(TDWC)是 Tivoli Workload Scheduler(TWS)的图形用户界面。这个小节将展示 TDWC v.8.5 是如何利用 Dojo 的能力的。 Tivoli Workload Scheduler 简介TWS 是一个生产自动化的解决方案,被设计用来在当今繁杂的操作环境里帮助管理工作量。主要的调度元素包括作业和作业流。作业代表的是一个任务,例如一个可执行的文件、程序或命令。作业流代表的是相关作业的容器,并按运行时、顺序、并行限制和重复性组织这些作业。TWS 用来帮助计划作业的执行,解决相互依赖性,启动并跟踪每个作业。一旦作业的依赖性得到满足,这些作业就会开始;这样,就最小化了空闲时间,并显著改进了吞吐量。作业永远不会打破顺序运行,并且如果一个作业失败了,TWS 可以在操作员少量参予甚至不参予的情况下对之进行恢复。 使用 DojoTDWC v.8.5 包括一个功能完善的工作量编辑器,这个编辑器是使用 Dojo Toolkit 完全以 JavaScript 编写的。这种实现使 “智能” 和行为更加贴近用户,让代码尽可能多地在浏览器内运行。 让我们看一个由 TDWC 显示的 TWS 作业流的示例属性面板。图 3 显示了针对作业流 PAYROLL 定义的通用属性的 Web 面板。 图 3. 作业流 PAYROLL 的 Web 面板在前一篇文章中(“The Abstract User Interface Markup Language Web Toolkit: An AUIML renderer for JavaScript and Dojo”,参见参考资料),我们介绍了如何用 AUIML 工具箱设计面板以及如何用 AUIML Web Toolkit(AWT)在 JavaScript 内实现逻辑代码。图 4 显示了 AUIML 面板: 图 4. AUIML 面板让我们着重看看Valid from字段。它已经在 AUIML 编辑器中被定义为 Edit Box,Date;AWT 通过清单 15 所示的 HTML 代码连接此元素。 清单 15. 用来连接元素的 HTML 代码<span type='text' dojoType='ajaxcommon.widgets.DateInputBox' id='validFrom'> <script type='dojo/method'event='onValueChanged'>AWT.dispatchOnChange(this.id);</script> </span> 清单 16 显示了小部件ajaxcommon.widgets.DateInputBox是如何定义的: 清单 16. 定义 ajaxcommon.widgets.DateInputBox 所需代码dojo.provide("ajaxcommon.widgets.DateInputBox"); dojo.require("ajaxcommon.resources.Images"); dojo.require("ajaxcommon.widgets._DateTimePicker"); dojo.require("ajaxcommon.widgets.picker.PickerInputBox"); dojo.require("ajaxcommon.widgets.picker.DatePicker"); dojo.declare( "ajaxcommon.widgets.DateInputBox",[ajaxcommon.widgets.picker.PickerInputBox,ajaxcommon.widgets._DateTimePicker],{ /** the picker icon */ pickerIcon: ajaxcommon.resources.Images.get()["CALENDAR_ICON"],/** the picker disabled icon */ pickerDisabledIcon: ajaxcommon.resources.Images.get()["DISABLED_CALENDAR_ICON"],/** the picker icon title */ pickerIconTitle: ajaxcommon.resources.Labels.get()["PICK_DATE"],/** constraints */ constraints: {selector: "date",formatLength: "short",wideYear: true},/** * Constructor. */ constructor: function() { // even if the format length is short,ensure the wide year (yyyy) // by overriding the datePattern if (this.constraints.formatLength === "short" && this.constraints.wideYear) { this.constraints.datePattern = this._getWideDatePattern(); } // set the regex for the text this.textRegExp = "^(" + dojo.date.locale.regexp(this.constraints) + “){0,1}$"; // set the regex message for the text this.textRegExpMessage = this._labels.format("DATE_INVALID_VALUE",{example: this._getExampleValue()}); // set the picker class this.pickerClass = "ajaxcommon.widgets.picker.DatePicker"; },/** * Returns the date pattern with the wide-year (yyyy) */ _getWideDatePattern: function() { // get the bundle var bundle = dojo.date.locale._getGregorianBundle(); // get the pattern var pattern = bundle["dateFormat-short"]; // replace the yy to yyyy if not yet yyyy if ( pattern.search(/yyyy/gi) === -1 ) { // the year is not in the wide form pattern = pattern.replace(/yy/gi,"yyyy"); } return pattern; },/** * Returns a string containing an example of accepted value. */ _getExampleValue: function() { // get a sample date object (my birthday!!!) var d = new Date(); d.setDate(15); d.setMonth(4); d.setYear(1971); // format the date in the current locale and return return dojo.date.locale.format(d,this.constraints); } } ); 这个构造函数(在清单 17中定义)允许初始化在超类小部件 清单 18 给出了相关的小部件模板。 清单 18. 面向 ajaxcommon.widgets.picker.PickerInputbox 的小部件模板<span class="picker" ><span tabindex="${tabindex}" dojoType="${_textBoxWidget}" dojoAttachPoint="_textNode,_focusNode" required="${required}" size="${size}" minLength="${textMinLength}" maxLength="${textMaxLength}" regExp="${textRegExp}" regExpReference="${textRegExpReference}" regExpMessage="${textRegExpMessage}" minWidth="${minWidth}" ></span ><span dojoAttachPoint="_pickerButtonContainer"> <img tabindex="${tabindex}" src="${pickerIcon}" dojoAttachPoint="_pickerButtonNode" dojoAttachEvent="onclick:_onClick,onkeyup:_onKeyUp" class="pickerButton" title="${pickerIconTitle}" /></span> </span> 图 5 显示了将小部件逻辑委派到浏览器端的好处。这个小部件能够显示相关的错误消息,如果在设定其值时发生错误,还能改变观感。 图 5. 显示了浏览器端小部件逻辑的面板回页首
结束语本文介绍了使用 Dojo JavaScript 工具箱开发 HTML 小部件的基本概念,从版本 1.0 和一些简单的小部件开始,逐渐过渡到较为复杂的小部件(比如在 TDWC v.8.5 内实现的小部件)。本文还着重强调了 Dojo 工具箱的几个要点。 在下一篇文章中,我们将讨论在开发富 Internet 应用程序期间经常遇到的一些缺陷,并将展示如何使用我们所开发的最佳实践(主要基于 Dojo 工具箱) 来解决这些缺陷。 参考资料学习
获得产品和技术
来源:http://www.ibm.com/developerworks/cn/web/wa-aj-dojotool/ (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ruby-on-rails – 在应用程序中使用rails engine部分
- Oracle数据库关联查询
- c# – 如何获得当月的最后一天和下个月的最后一天
- ruby – RVM:在系统范围的安装中从.rvmrc文件加载gemset时
- ruby-on-rails – Rails 4强参数,包含多个对象和整数键
- nosql数据库学习总结
- gdb调试ns-3 netanim中的写入xml
- ggsci: error while loading shared libraries: /u01/app/o
- MediaPlayer/MediaPlayer 视频播放
- reactjs – React.js中的一个简单的“Hello World”无效