模板方法可以由实现者提供,但必须注意的一点是超类有可能已经实现了其模板方法。要确保对链中所有类的正确初始化,实现者必须手动编写超类方法调用的代码,如清单 11 所示。
清单 11. 超类方法调用的示例
startup: function() { // call the superclass' method this.inherited("startup", arguments); // your code here } |
作为一个通用约定,对于所有创建方法(除 uninitialize 以外的所有模板方法),超类的方法应第一个被调用以确保在处理超类的条目前它们已被正确地初始化。
uninitialize 方法则要遵照相反的规则。
最后一个要注意的是 startup 方法。别忘了,若小部件通过声明被创建,startup 方法是通过 dojo.parser.parse 方法自动调用的。倘若小部件是由编程方式创建,情况就不一样了,必须像下面例子所示的那样手动完成。
var w = new MyWidget({}); w.startup(); |
小部件销毁阶段
创建后,小部件将一直存在,直到执行一个显式的销毁请求。实现者负责管理小部件的整个生命周期,包括小部件的销毁。否则,将会导致有遗漏的小部件存在,直到整个页面清理发生。这个小部件提供了四种销毁方法:
- destroyRendering:这个方法用来清理小部件的呈现条目。除非实现者需要的是部分清理,否则该方法往往由其他摧毁方法调用。
- destroyDescendants:销毁所有的子小部件。除非实现者需要的是部分清理,否则该方法由 destroyRecursive 方法调用。
- destroy:销毁所有的小部件条目(不包括子小部件)。
- destroyRecursive:销毁小部件条目及子小部件。如果小部件包含有内部小部件,那么必须调用此方法。
在本示例中,小部件包含有内部小部件(例如:在其模板中有一些小部件),所以实现者必须要记得触发子小部件,以便销毁这些子小部件并且无需让小部件用户决定调用何种销毁方法。清单 12 给出了实现此目的一种可能方式。
清单 12. 触发小部件销毁的示例
uninitialize: function() { // destroy the descendants this.destroyDescendants(); // call the superclass' method this.inherited("uninitialize", arguments); } |
事件管理
Dojo 小部件可以对来自于 DOM 节点或对象的外部事件做出反应。这类事件可通过使用小部件的 connect 方法被手动连接,该方法具有如下所示的签名(非常类似于 dojo.connect 方法):
connect: function(/*Object|null*/ obj, /*String*/ event, /*String|Function*/ method); |
或者,也可以通过使用 dojoAttachEvent 属性在模板中声明此连接的方法进行自动连接。在这两种情况下,此连接都可以由小部件在 _connects 数组内内部跟踪。所有连接在销毁时都将会自动断开。这样,小部件与 DOM 节点间的相互引用就被打破了。
如果,在小部件生命周期内一些连接已经不再需要,那么实现者就可以通过调用 disconnect 方法来手动断开这些连接以减少事件的处理。
小部件模板
如前面看到的,小部件必须继承自 dijit._Widget 类,它定义并提供了 Dojo 小部件的基本行为。这样一个基类定义了负责构建小部件呈现元素的 buildRendering 方法。比如,一个小部件实现者可以用这种方法创建小部件标记并将其设置到小部件 DOM 节点。另一种方案是使用 DOM API 创建小部件结构。在这两种情况下,实现者都必须以某种方式编程实现 buildRendering 方法。
Dojo 提供了一个强大的抽象,即 dijit._Templated 混入类,可以将小部件呈现定义与小部件行为实现分离开来。想要开发这样一个抽象,实现者需要从 _Templated 类继承,如清单 13 所示。
清单 13. 从 _Templated 类继承
dojo.declare( "widgets.MyWidget", dijit._Widget, dijit._Templated { templatePath: dojo.moduleUrl("widgets", "templates/MyWidget.html"), } ); |
_Templated 类提供了它自身的 buildRendering 实现,该实现利用了一个类似 HTML 的定义。这个定义有可能存在于两个不同的地方。
- 一个外部文件。在这种情况下,该文件由 templatePath 属性引用。
- 一个内部字符串属性。在这种情况中,模板直接通过 templateString 属性在小部件内定义。如果指定了 templateString,那么 templatePath 可被忽略,即便指定,也是如此。
第一种选择是一种组织小部件源代码的最干净的方法,因为这个标记可被写入一个不同的文件,也可被以一种可读的方式格式化,同时不必费心于字符串的串联,并且字符串分界符不可转义。而第二种选择要更好一些,因为无需从浏览器载入第二个文件。然而,您不必担心,因为 Dojo 提供了一个构建工具,能将外部模板内部化到小部件源代码。
一个模板可以通过引用小部件属性而被参数化。在小部件公开某些能直接影响此标记的配置参数时,或是这个标记必须要依照外部首选项而有条件地生成时,这一点将会很有用。小部件属性通过使用这样的语法引用:${propertyName}。
模板可以包含其他的小部件声明。不过,为了将它们考虑进去,小部件开发人员必须将 widgetsInTemplate 属性设为 true,该属性默认情况下被设为 false 以便跳过并非必需的处理。
模板可以包含如下两种特殊的属性声明:
- dojoAttachPoint:如果指定,这个标记属性必须被设置为一个小部件属性名。对应于此标记的 DOM 将被设置为此小部件的属性。模板内的几个标记可以有一个或多个附加点。
- dojoAttachEvent:这个标记属性会列出在触发 DOM 事件时必须被回调的那些小部件方法。
清单 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 与 Dojo
Tivoli Dynamic Workload Console(TDWC)是 Tivoli Workload Scheduler(TWS)的图形用户界面。这个小节将展示 TDWC v.8.5 是如何利用 Dojo 的能力的。
Tivoli Workload Scheduler 简介
TWS 是一个生产自动化的解决方案,被设计用来在当今繁杂的操作环境里帮助管理工作量。主要的调度元素包括作业和作业流。作业 代表的是一个任务,例如一个可执行的文件、程序或命令。作业流 代表的是相关作业的容器,并按运行时、顺序、并行限制和重复性组织这些作业。TWS 用来帮助计划作业的执行,解决相互依赖性,启动并跟踪每个作业。一旦作业的依赖性得到满足,这些作业就会开始;这样,就最小化了空闲时间,并显著改进了吞吐量。作业永远不会打破顺序运行,并且如果一个作业失败了,TWS 可以在操作员少量参予甚至不参予的情况下对之进行恢复。
使用 Dojo
TDWC 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> |