引言
Dojo 工具包为程序员提供了很多功能丰富的控件,但是在实际应用中,很多时候程序员需要自定义控件来满足实际需求,如开发统一 UI 风格的控件库,开发具有通用逻辑组合的 Dojo 控件和更方便使用的 Dojo 控件库。自定义的控件可以在项目、团队中复用,从而可以充分提高开发效率和增加可维护性。
准备工作
在创建自定义控件之前需要就有以下知识:
了解 Dojo 和 Dojo 工具包
请参考:http://dojotoolkit.org/reference-guide/quickstart/
Dojo 开发环境的搭建
下载 Dojo 工具包,http://dojotoolkit.org/download/
调试工具安装,如 FireFox 浏览器下的 FireBug
开发和调试 Dojo 程序的基本步骤:
使用 djConfig 加载 Dojo,请参考:http://dojotoolkit.org/reference-guide/djConfig.html#djconfig
使用 dojo.require 加载 Dojo 控件,请参考:http://dojotoolkit.org/reference-guide/dojo/require.html#dojo-require
申明和使用 Dojo 控件,请参考:http://dojotoolkit.org/reference-guide/dojo/index.html
异常和调试,使用 try/catch 捕获异常,在 FireBug 中使用 console 进行日志记录和跟踪
创建自定义控件基础知识
在创建一个自定义控件时要使用到 Dojo 工具包的 dojo.provide、dojo.declare 函数和基础控件 Dijit._Templated、Dijit._widget,本节将介绍它们。
dojo.provide
dojo.provide 函数用于定义 Dojo 模块,dojo.provide 函数的定义方式为:
dojo.provide("模块名称");
Dojo 模块的名称在当前 Dojo 包下必须唯一,而且定义模块时模块名和模块的文件名称必须相同。
例如定义 my.module 模块,在 my/module.js 模块文件中代码如下:
清单 1. 使用 dojo.provide 定义 Dojo 模块
<scripttype="text/javascript"> dojo.provide("my.module"); dojo.require("dojo.io.script"); my.module.name="mymodule"; </script> |
dojo.declare
dojo.declare 函数使用类似面向对象的方式定义 JavaScript 类,但是它不是面向对象的。
表 1. dojo.declare 参数
参数名 | 数据类型 | 含义 |
---|---|---|
类名 | String | 定义的类的名称 |
null | ||
父类 | null | 没有父类 |
Object | 一个父类 | |
Object[] | 多个父类 |
在 dojo.declare 中可以定义 constructor 函数用于申明类初始化时需要执行的代码,这很类似于 Java 类的构造方法。
下面是一个使用 dojo.declare 定义类和使用定义类的简单实例:
清单 2. 使用 dojo.declare 定义类和使用类
dojo.declare("my.FirstSample",null,{ name:null,constructor:function(args){ dojo.safeMixin(this,args); },setName:function(name){ this.name=name; } }); varsample1=newmy.FirstSample({name:"Jack"});//创建对象 console.log(sample1.name); sample1.setName("Mark");//调用对象函数 console.log(sample1.name); |
函数定义方式为,“函数名:function( 参数 ){ 函数体 }”,如 my.FirstSample 中定义的函数
setName:function(name){ this.name=name; }
使用 dojo.declare 定义继承 |
dojo.declare 可以定义当前类继承的父类,与面向对象不同的是可以是继承多个父类。
如果在子类中覆写了父类的方法,可用 this.inherited(arguments) 来调用父类的同名方法。
清单 3. 使用 dojo.declare 定义继承示例
dojo.declare("A",{ constructor:function(){console.debug("mixinginA");} }); dojo.declare("B",{ constructor:function(){console.debug("mixinginB");},kind:"typeb",run:function(){ } }); dojo.declare("C",[A,B,],{ constructor:function(){ console.debug("Ablizzardwith"+ this.kind+"MandMsand" ); } run:function(){ //callbaseclassrun this.inherited(); //nowdosomethingelse } }); |
dijit._Templated 根据指定的 HTML 模板来创建 Widget 的 DOM 树,它可以作为创建 Widget 时的辅助。
指定 Widget 对应的 HTML 模板的方法有以下两种:
清单 4. 在 templateString 属性中申明 DOM 对象字符串
dojo.declare("MyWidget",[dijit._Widget,dijit._Templated],{ templateString:"<div>helloworld</div>" }); |
清单 5. 在 templateString 属性中引用创建好的 HTML 文件
dojo.declare("MyWidget",{ templateString:dojo.cache("myNameSpace","templates/MyWidget.html"),}); |
在 HTML 文件中对象的唯一标示为 ID,在 Widget 的 HTML 模板中,可以用 dojoAttachPoint 属性来标示 DOM 对象,在 Widget 对象中可以直接用 this.${dojoAttachPoint} 来操作 DOM 对象,如清单 6 中的定义,可用 this.rootNode 来引用外层 div 对象,如:
清单 6. HTML 模板文件中的声明
<divdojoAttachPoint="rootNode"> <divdojoAttachPoint="chartNode"> </div> <divdojoAttachPoint="legendHolder"> </div></div> |
dijit._ widget
dijit._widget 是所有 Widget 的基础类,在 Widget 的生命周期中重要的方法如下:
1 ) constructor 方法
创建 Widget 时首先执行的方法,此时你的 Widget 中变量还没有被赋值所以最好不要此时操作 Widget 的变量;
2 ) postMixInProperties 方法
在 DOM 对象创建前和 render 发生前执行,此时你的 Widget 的变量已经赋值,如果你想在控件的 DOM 对象创建前对 Widget 的变量进行处理,可以在子类中覆写这个方法;
3 ) postCreate 方法
在 render 之后,但是子 Widget 还没有创建时执行。此时你可以操作当前 Widget 的 DOM 对象,所以大部分可以初始化操作可以在这处理,如绑定事件和设置 CSS 属性。
注意:此时 Widget 可能还没有放到 DOM 树中,因此在此函数中不要处理 DOM 对象的大小。
4 ) startup 方法
在所有的子 Widget 创建完成和 parsing 完成后执行;
5 ) destroy 方法
Widget 销毁前执行。
Widget 事件管理
Widget 可以对来自于 DOM 节点或对象的外部事件做出反应。这类事件可通过使用 Widget 的 connect 方法被手动连接,该方法如下(非常类似于 dojo.connect 方法):
connect:function(/*Object|null*/obj,/*String*/event,/*String|Function*/method); |
如果在 Widget 生命周期内事件连接不需再要,可以调用 disconnect 方法来手动断开连接 .
Widget 的事件定制可以用 Widget 的 subscribe 方法来实现,改方法如下(类似于 dojo.subscribe 方法):
subscribe:function(/*String*/ topic,/*String|Function*/ method);
如果在 Widget 的生命周期内事件定制不需要,可以调用 unsubscribe 来取消定制。
注意 :
1)在自定义的 Widget 中,要进行初始化操作时覆写 postCreate 方法。
2)在进行 Widget 销毁前处理时,最好不好覆盖父类的 destroy,因为 _widget 类的 destroy 方法会自动销毁 Widget 及子 Widget 相关的资源,可以复写 uninitialize 方法来销毁需要手动销毁的资源,在 destroy 时会调用 uninitialize 方法。
3)在使用 Widget 时,如果需要绑定事件或者定制事件时可以用 Widget 的 connect 和 subscribe 方法,在 Widget 销毁时会销毁相关的 connect 和 subscribe 句柄。如果需要手动销毁事件句柄可用 disconnect 和 unsubscribe 方法。
赋值:
取值:
定义自定义控件步骤
根据第二节的介绍,本节将以 Radio Widget 为例介绍创建一个自定义控件的步骤。在 Dojo 库中有 dijit.form.RadioButton 这个控件,但是使用起来比较麻烦,下面是一个改进的自定义 RadioButton 的创建过程。
使用 dojoAttachPoint 属性定义 DOM 对象的唯一 ID,文件代码如下:
<divdojoAttachPoint="myRatioNode"></div> |
第二步,创建 RadioGroup.js 文件,创建步骤为:
1 引用 Dojo 工具包控件:
清单 8. 引用 Dojo 工具包控件
dojo.provide("health.RadioGroup"); dojo.require("dijit._Widget"); dojo.require("dijit._Templated"); dojo.require("dijit.form.CheckBox"); |
2 定义 health.RadioGroup 类
使用 dojo.declare 定义 health.RadioGroup 类,继承 dijit._Widget 和 dijit._Templated
清单 9. health.RadioGroup 定义语句
dojo.declare("health.RadioGroup",dijit._Templated] |
3 绑定模板文件
使用 dojo.cache 绑定第一步中定义的模板文件
清单 10. 绑定模板文件
templateString:dojo.cache("health","templates/RadioGroup.html")|-------10--------20--------30--------40--------50--------60--------70--------80--------9||--------XMLerror:TheprevIoUslineislongerthanthemaxof90characters---------| |
4 定义成员变量
定义变量时需要对变量进行初始化,如果无初始化值给变量赋值 null,格式如下:
清单 11. 定义成员变量
onChangeCallback:null,//当前Widget的成员变量 id:null,relativeChart:null,relativeChartGroup[]:null,name:"testName",//在定义变量时可以进行赋值 |
5 定义初始化函数
覆写构造函数 constructor(根据需要覆写);
覆写初始化函数 postCreate(根据需要覆写);
constructor:function(params){//构造函数 this.relativeChartGroup=[]; },postMixInProperties:function(){ if(this.name==null){//在此时可以获取到变量的值 this.name="testName"; } },postCreate:function(arguments){ console.debug("myRatioNode==",this.myRatioNode); }, |
定义函数的格式为 :"函数名 : function( 参数 1,参数 2,参数 3,...)"。
addItem:function(value,lableText,checked){ if(checked==null){ checked=false; } console.debug("checked==",checked); console.debug("value==",value); console.debug("name==",this.name); varself=this; varradioOne=newdijit.form.RadioButton({ checked:checked,value:value,name:self.id,onChange:function(checked){ if(checked){ self.onChangeCallback(this.value); } } }); this.domNode.appendChild(radioOne.domNode); varlabel=document.createElement("label"); label.innerHTML=lableText; this.domNode.appendChild(label); } });注意:在函数中引用 widget 的变量或者调用其他函数时要用"this. 变量名 / 函数名"。 |
7 覆写 uninitialize 方法
在 uninitialize 中释放和销毁相关资源。
清单 14. Uninitialize 方法
uninitialize:function(arguments){ If(this.relativeChart) this.relativeChart.destroy(); },应用自定义的控件 |
第一步,引入自定义控件
在应用中引入自定义控件的方式有两种:
1) 将自定义的控件打包到 Dojo 包中
请参考:http://www.ibm.com/developerworks/cn/web/0912_shenjc_dojobuild/
2) 用 dojo.registerModulePath 注册自定义模块;
用 dojo.require 引用自定义控件。
例如,在第 3 节中创建的 Widget 可以用如下方式引用
清单 15. 引入自定义控件
dojo.registerModulePath("health","[path]/health");//注册自定义模块 dojo.require("health.RadioGroup");//引用自定义控件第二步,使用自定义控件 |
1)用 new 创建自定义控件实例;
清单 16. 创建自定义控件
varrateSelectRadio=newhealth.RadioGroup({ id:"rate_radio",relativeChart:chart }); rateSelectRadio.addItem("weekly","周基准",true); rateSelectRadio.addItem("monthly","月基准",false);图 1:Radio Group 控件代码效果 |
在未使用自定义控件前,要绘制一个如图 1 效果的 Radio Group 代码如下:
清单 17. 未使用自定义控件时创建 Radio Group 的代码
varradioContainerNode=document.createElement("div"); varradioOne=newdijit.form.RadioButton({ checked:checked,value:"weekly",name:"rate_radio",onChange:function(checked){ if(checked){ doChangeRate(this.value); }}}); this.domNode.appendChild(radioOne.domNode); varlabel=document.createElement("label"); label.innerHTML="周基准"; radioContainerNode.appendChild(label); varradioOne=newdijit.form.RadioButton({ checked:checked,value:"monthly",onChange:function(checked){ if(checked){ doChangeRate(this.value); }}}); this.domNode.appendChild(radioOne.domNode); varlabel=document.createElement("label"); label.innerHTML="月基准"; radioContainerNode.appendChild(label);对比清单 16 和清单 17,可见使用自定义控件减少了开发工作。 |
结束语
使用自定义控件,可以充分复用 Dojo 代码,降低代码冗余,提高效率。建议大家在实际应用中,充分利用 Dojo 这一特征,写出干净漂亮的代码。
转载自https://www.oschina.net/question/129540_28352