0
回答
Dojo 的代码重用
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

引言

Dojo 工具包为程序员提供了很多功能丰富的控件,但是在实际应用中,很多时候程序员需要自定义控件来满足实际需求,如开发统一 UI 风格的控件库,开发具有通用逻辑组合的 Dojo 控件和更方便使用的 Dojo 控件库。自定义的控件可以在项目、团队中复用,从而可以充分提高开发效率和增加可维护性。

准备工作

在创建自定义控件之前需要就有以下知识:

  1. 了解 Dojo 和 Dojo 工具包

    请参考:http://dojotoolkit.org/reference-guide/quickstart/

  2. Dojo 开发环境的搭建

    下载 Dojo 工具包,http://dojotoolkit.org/download/

    调试工具安装,如 FireFox 浏览器下的 FireBug

  3. 开发和调试 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 模块
 <script type="text/javascript"> 
  dojo.provide("my.module"); 

  dojo.require("dojo.io.script"); 

  my.module.name = "my module"; 
 </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; 
  } 
 }); 
 var sample1= new my.FirstSample({ name:"Jack"}); // 创建对象
 console.log(sample1.name); 
 sample1.setName("Mark"); // 调用对象函数
 console.log(sample1.name);

变量定义方式为,“变量名:变量值”,如 my.FirstSample 中定义的名称 name: null

函数定义方式为,“函数名:function( 参数 ){ 函数体 }”,如 my.FirstSample 中定义的函数

setName: function(name){ 
     this.name = name; 
}

使用 dojo.declare 定义继承

dojo.declare 可以定义当前类继承的父类,与面向对象不同的是可以是继承多个父类。

在默认情况下,父类的构造函数会在子类的构造函数之前执行。

如果在子类中覆写了父类的方法,可用 this.inherited(arguments) 来调用父类的同名方法。


清单 3. 使用 dojo.declare 定义继承示例
dojo.declare("A", null, { 
  constructor: function() { console.debug ("mixing in A"); } 
 }); 
 dojo.declare("B", null, { 
  constructor: function() { console.debug("mixing in B"); }, 
  kind: "type b", 
  run: function() { 
 } 
 }); 
 dojo.declare("C", [A, B,], { 
      constructor: function() { 
           console.debug("A blizzard with " + 
               this.kind + " M and Ms and " 
           ); 
      } 
 run: function() { 
  // call base class run 
  this.inherited(); 
  // now do something else 
 } 
 }); 

dijit._Templated

dijit._Templated 根据指定的 HTML 模板来创建 Widget 的 DOM 树,它可以作为创建 Widget 时的辅助。

指定 Widget 对应的 HTML 模板的方法有以下两种:


清单 4. 在 templateString 属性中申明 DOM 对象字符串
 dojo.declare("MyWidget", [dijit._Widget, dijit._Templated], { 
      templateString: "<div>hello world</div>"
  }); 

清单 5. 在 templateString 属性中引用创建好的 HTML 文件
				
 dojo.declare("MyWidget", [dijit._Widget, dijit._Templated], { 
      templateString: dojo.cache("myNameSpace", "templates/MyWidget.html"), 
 }); 

在 HTML 文件中对象的唯一标示为 ID,在 Widget 的 HTML 模板中,可以用 dojoAttachPoint 属性来标示 DOM 对象,在 Widget 对象中可以直接用 this.${dojoAttachPoint} 来操作 DOM 对象,如清单 6 中的定义,可用 this.rootNode 来引用外层 div 对象,如:


清单 6. HTML 模板文件中的声明
 <div dojoAttachPoint="rootNode">
 <div dojoAttachPoint="chartNode">
 </div >
 <div dojoAttachPoint="legendHolder">
 </div></div> 

注意:定义 Widget 的 HTML 模板时根节点只能有一个,不要在根节点外写注释,在每行的结束时不能有’ > ’符,除非是最后一行。

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 方法。

4)对 Widget 的属性操作方法有:

  • 赋值:
    • 赋值方式一,在构造方法中赋值, 如:var fSmaple = new my.FirstSample({ name:"Jack"}); fSmaple 对象的 name 属性将在创建对象时被赋值;
    • 赋值方式二,创建完对象后赋值,attr("属性名",“属性值”),如:fSmaple.attr("name","Jack");dojo1.5 及以上版本推荐使用 widget.set('property', 'value');
  • 取值:
    • 获取 Widget 属性值用 attr("属性名"),如:fSmaple.attr("name");
    • Dojo1.5 及以上版本推荐使用 widget.get('property');

定义自定义控件步骤

根据第二节的介绍,本节将以 Radio Widget 为例介绍创建一个自定义控件的步骤。在 Dojo 库中有 dijit.form.RadioButton 这个控件,但是使用起来比较麻烦,下面是一个改进的自定义 RadioButton 的创建过程。

第一步,创建模板文件 RadioGroup.html

使用 dojoAttachPoint 属性定义 DOM 对象的唯一 ID,文件代码如下:


清单 7. RadioGroup 模板文件代码
				
 <div dojoAttachPoint="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._Widget, dijit._Templated]

3 绑定模板文件

使用 dojo.cache 绑定第一步中定义的模板文件


清单 10. 绑定模板文件
templateString: dojo.cache("health", "templates/RadioGroup.html")                              |-------10--------20--------30--------40--------50--------60--------70--------80--------9| |-------- XML error:  The previous line is longer than the max of 90 characters ---------| 

4 定义成员变量

定义变量时需要对变量进行初始化,如果无初始化值给变量赋值 null,格式如下:


清单 11. 定义成员变量
   onChangeCallback : null,      // 当前 Widget 的成员变量
   id: null, 
   relativeChart: null, 
   relativeChartGroup[]:null, 
   name: "testName",           // 在定义变量时可以进行赋值

5 定义初始化函数

覆写构造函数 constructor(根据需要覆写);

覆写初始化函数 postCreate(根据需要覆写);

代码如下:


清单 12. 定义构造函数和初始化函数   
constructor: function(params) {//构造函数
    this.relativeChartGroup = [];
  },
  
  postMixInProperties: function(){
      if(this.name==null){         //在此时可以获取到变量的值
          this.name = "testName";
      }
  },
  
  postCreate: function(arguments){
     console.debug("myRatioNode==",this.myRatioNode);
  },


6 定义添加 Radio 选项函数

定义函数的格式为 :"函数名 : function( 参数 1, 参数 2, 参数 3,...)"。


清单 13. 添加 Radio 选项函数   
addItem: function(value,lableText,checked){
    if(checked == null){
       checked = false;
    }
    console.debug("checked==",checked);
    console.debug("value==",value);
    console.debug("name==",this.name);
    
    var self = this;
    var radioOne = new dijit.form.RadioButton({
                                  checked: checked,
                                  value: value,
                                  name: self.id,
                                  onChange: function(checked){
                                     if(checked){
                                         self.onChangeCallback( this.value);
                                     }
                                  }
                            });
    this.domNode.appendChild(radioOne.domNode);
    var label  = 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 创建自定义控件实例;

2)在创建实例时用“属性名:属性值”初始化实例变量;

3)在创建完自定义控件实例后调用对象的方法。

使用自定义控件代码为:


清单 16. 创建自定义控件
var rateSelectRadio = new health.RadioGroup({ 
                                         id:"rate_radio", 
                                         relativeChart:chart 
                                         }); 
   rateSelectRadio.addItem("weekly","周基准",true); 
   rateSelectRadio.addItem("monthly","月基准",false); 

图 1:Radio Group 控件代码效果
图 1:Radio Group 控件代码效果

应用自定义控件的创建方法的好处

在未使用自定义控件前,要绘制一个如图 1 效果的 Radio Group 代码如下:


清单 17. 未使用自定义控件时创建 Radio Group 的代码
 var radioContainerNode= document.createElement("div"); 
 var radioOne = new dijit.form.RadioButton({ 
                                  checked: checked, 
                                  value: "weekly", 
                                  name: "rate_radio", 
                                  onChange: function(checked){ 
                                     if(checked){ 
                                         doChangeRate( this.value); 
                                     }}}); 
    this.domNode.appendChild(radioOne.domNode); 
    var label  = document.createElement("label"); 
    label.innerHTML = "周基准"; 
    radioContainerNode.appendChild(label); 
    var radioOne = new dijit.form.RadioButton({ 
                                  checked: checked, 
                                  value: "monthly", 
                                  name: "rate_radio", 
                                  onChange: function(checked){ 
                                     if(checked){ 
                                         doChangeRate( this.value); 
                                     }}}); 
    this.domNode.appendChild(radioOne.domNode); 
    var label  = document.createElement("label"); 
    label.innerHTML = "月基准"; 
    radioContainerNode.appendChild(label); 

对比清单 16 和清单 17,可见使用自定义控件减少了开发工作。

结束语

使用自定义控件,可以充分复用 Dojo 代码,降低代码冗余,提高效率。建议大家在实际应用中,充分利用 Dojo 这一特征,写出干净漂亮的代码。

文章出处:IBM developerWorks

举报
IBMdW
发帖于6年前 0回/1K+阅
顶部