Dojo开发包本身提供了多种widget,它们涵盖了大部分在web开发中可能会使用的界面组件,如下拉菜单、树形菜单等,开发人员只需要在JavaScript代码中声明对特定widget的引用,就可以使用标记的方式在页面中加入widget。当然这些widget并不一定能完成满足实际应用的需要,开发人员也可以通过继承的方式在现有widget的基础上进行功能扩展。
此外,在web应用中可能会有多处地方使用相同或者类似的界面,因此在页面中可能会有大量类似或者重复的代码。这时开发人员可以定义新的widget,在页面中使用标记的方式实现界面代码的重用。
1. 扩展Dojo自带widget
第一种自定义widget的方法是扩展Dojo开发包自带的widget,下面以按钮widget为例进行widget的扩展。我们的目标是修改按钮的显示风格,包括各种状态下按钮的背景图片。
(1) 定义widget子类
首先,在test\mywidget目录下新建一个JavaScript代码文件Button.js,其中定义了一个继承自dojo.widget.Button的widget,使用的函数是dojo.widget.defineWidget,具体代码如下:
//扩展widget的包名 Dojo.provide(“my.widget.Button”); Dojo.require(“dojo.widget.Button”); //定义一个新的widget Dojo.widget.defineWidget( //类名 “my.widget.Button”, //父类 Dojo.widget.Button, //覆盖父类的若干属性 { //更换按钮的背景图片 InactiveImg: “test/mywidget/inactive”, activeImg: “test/mywidget/active-”, pressedImg: “test/mywidget/pressed-”, diaabledImg: “test/mywidget/diaabled-”, } ); |
上面的代码通过覆盖父类的相关属性更换了4种状态(inactive、active、pressed、disabled)下按钮的背景图片。
(2) 注册模块路径
Button.js的代码指明扩展的widget对应的类是my.widget.Button,因此使用这个widget必须引入my.widget.Button类。仅仅通过dojo.require语句是不够的,Dojo并不知道Button.js代码所在的路径,因此首先要调用dojo.registerModulePath方法注册模块路径。模块路径的概念相当于java中的类路径,Dojo会在模块路径下寻找相关类的执行代码,模块路径的起始位置是dojo.js所在的目录。注册模块路径的代码如下所示:
Dojo.registerModulePath(“my.widget”, “test/mywidget”); Dojo.require(“my.widget.Button”); |
(3) 使用widget
现在可以用标记的方式在页面中调用自定义的按钮widget,例如:
<button dojoType=”my:Button”> <div class=”text” style=”width:50px;height:20px”>button</div> </button> |
这里使用my作为Button的前缀,与Dojo自带的Button widget所示区别。
2. 定义新的widget
某些情况下,在Dojo自带的widget的基础上进行扩展不能满足应用的需要,这时就需要从头开始打造一个全新的widget。一个widget的定义通常包括以下内容:
1) 显示模板(HTML页面和CSS样式);
2) 内容和属性的定义;
3) 事件的定义。
下面介绍开发自定义widget的一般步骤。以实现简单的便签页面组件为例。如图1所示。
图1 自定义widget效果图
在页面中可以通过如下的代码调用该widget。
<div dojoType=”my:Memo” title=”测试标题”> 文本内容…… </div> |
(1)定义widget的显示模板
自定义widget的第1步是准备widget的显示模板,它一般由两部分组成:HTML页面和CSS样式表。相关代码比较简单,如下所示:
Memo.html: <div class=”memo”> <div class=”title”>Memo Title</div> <div class=”close”>X</div> <div class=”contents”> </div> </div> Memo.css(略) |
(2)声明widget
第2步是在JavaScript代码中给出widget的定义,对于在HTML页面中使用的widget来说,它们的基类是dojo.widget.HtmlWidget。自定义的widget类必须覆盖基类dojo.widget.HtmlWidget的两个属性:templatePath和templateCssPath,它们分别是HTML模板和CSS模板文件的路径。具体实现代码(Memo.js)如下所示:
//给出自定义widget的类名 Dojo.provide(“my.widget.Memo”); Dojo.require(“dojo.widget.HtmlWidget”); Dojo.widget.defineWidget( //widget对应的类 “my.widget.Memo”, //widget的基类是Dojo.widget. HtmlWidget Dojo.widget. HtmlWidget, //覆盖基类的属性 { //模板HTML的路径 templatePath: dojo.uri.dojoUri(“test/mywidget/Memo.html”), //模板CSS的路径 templateCssPath: dojo.uri.dojoUri(“test/mywidget/Memo.css”) } ); |
(3)处理widget的内容和参数
运行上面的代码,可以得知widget还存在两个问题:
1) Widget内部的内容完全被忽略,没有显示在页面上;
2) 便签的标题不能以属性的方式进行声明,始终显示固定的文字“Memo Title”。
在Dojo widget中只有容器类型的widget才能处理其内部的HTML代码。设置容器类型widget的方法是:覆盖基类Dojo.widget. HtmlWidget的属性isContainer,将其设置为true。对Memo.js进行修改,代码如下所示:
…… //覆盖基类的属性 { //该widget是一个容器 isContainer:true, //模板HTML的路径 templatePath: dojo.uri.dojoUri(“test/mywidget/Memo.html”), //模板CSS的路径 templateCssPath: dojo.uri.dojoUri(“test/mywidget/Memo.css”) } …… |
容器类型的widget会对其内部的HTML代码进行处理,具体如何显示以及显示在widget的哪一部分,则需要在HTML模板中进行配置。例如在便签widget中,如果希望它内部的HTML代码在widget内部的某个<div>元素中呈现,那么可以这样修改HTML模板的定义:
<div class=”memo”> <div class=”title”>Memo Title</div> <div class=”close”>X</div> <div class=”contents” dojoAttachPoint=”containerNode”> </div> </div> |
为了能够使用属性的方式声明便签的标题,需要在widget的定义中增加一个title属,性,同时将其添加到HTML模板中合适的位置。修改后的Memo.js和Memo.html代码如下所示:
Memo.js: …… //覆盖基类的属性 { //便签widget的标题 title:”Memo Title”, //该widget是一个容器 isContainer:true, //模板HTML的路径 templatePath: dojo.uri.dojoUri(“test/mywidget/Memo.html”), //模板CSS的路径 templateCssPath: dojo.uri.dojoUri(“test/mywidget/Memo.css”) } …… Memo.html: <div class=”memo”> <div class=”title”>${this.title}</div> <div class=”close”>X</div> <div class=”contents” dojoAttachPoint=”containerNode”> </div> </div> 其中${this.title}表示widget对象的title属性。 |
(4)定义widget的事件
Widget可以看作页面中相对独立的界面组件,它必须具有独立的事件响应机制,才能完成一些复杂的功能。对于便签widget来说,需要增加单击“关闭”按钮的onClick事件。事件的定义是在HTML模板中进行的,下面的代码为“关闭”按钮的<div>元素增加了一个onClick事件。
<div class=”close” dojoAttachEvent=”onClick”>X</div> |
事件的处理函数可以使用dojo.event.connect方法添加,例如:
Dojo.addOnLoad(function(){ //widget对象 Var memo=dojo.widget.byId(“memo”); //添加widget对象的onClick事件处理函数 Dojo.event.connect(memo, “onClick”, function(evt){ Memo.destroy(); }); }) |
Dojo widget的事件名称有“内外”之分,内部事件通常采用DOM事件的名称,例如onclick、onmouseover等;而外部事件名称则在为widget添加事件处理函数时使用,缺省情况下外部事件的名称与内部事件相同,如果需要指定外部事件的名称,则需要在相应的dojoAttachEvent属性中使用“内部事件名称:外部事件名称”的形式。例如将便签widget的HTML模板修改为如下的代码:
…… <div class=”close” dojoAttachEvent=”ondblclick:onclick”>X</div> …… |
此时当“关闭”按钮<div>元素被双击时(即触发内部事件ondblclick),widget的外部事件——onClick事件被触发,页面执行相应的处理函数。