在这个教程中,我们将会演示如何利用Dojo 和Dijit框架来创建自定义的小部件,主要会使用到dijit/_WidgetBase and dijit/_TemplatedMixin。

    Dojo的Dijit 库包含了丰富的界面小部件(Widgets),通过使用这些小部件,可以打造出强大的Web应用界面,从高级的表单元素,到复杂页面布局。

    假设我们需要开发一个能展示所有Dojo教程作者的简介信息的页面,我们手头的数据源是如下的JSON数据:

1
2
3
4
5
6
7
8
[
     {
         "name" "Brian Arnold" ,
         "avatar" "./myApp/widget/images/avatar.jpg" ,
         "bio" "Brian Arnold is a software engineer at SitePen, Inc., ..."
     },
     /* More authors here... */
]

    我们的需求是把这些信息以下面的DOM结构展示在页面上:

1
2
3
4
5
6
7
< body >
     <!-- Headers and whatnot -->
     < h2 >Authors</ h2 >
     < div  id = "authorContainer" >
         <!-- Authors go here! -->
     </ div >
</ body >

    当然,我们希望这个展示页面可以再加点效果,例如当鼠标移到某个作者上时,背景色可以淡入显示。最终展示的页面效果如下:

wKioL1RkkGSjWp5SAAJWTxzcCFQ482.jpg

解决方案:

    可以创建一个自定义的Dojo小部件来实现这一需求,分如下几个步骤:

1. 创建必要的小部件目录结构

2. 创建展示单个作者的HTML片段

3. 把HTML片段变成Dijit模板

4. 使用dojo/_base/declare来创建小部件类

5. 进行必要的CSS美化


1、为自定义小部件创建必要的目录结构

    虽然这一步看起来有些多余,但是为你开发的自定义小部件安排一个合理的目录结构是一种良好的编程习惯的。在这里我会创建一个名为myApp的目录,作为存放我所有代码和模板的命名空间。当然目录的名称完全取决于你自己,可以是你就职的机构的名称。然后我为即将创建的自定义小部件取名叫widget,所以我会创建一个widget目录,这个目录会包含与这个小部件相关的css和html模板以及源代码。最终的文件结构会如下图所示:

wKioL1RlsxWh0Ph1AAAxpS6AXlc584.jpg


2、创建展示单个作者的HTML片段

    这里我们使用如下的简单片段来展示一个作者的信息。最外层是一个容器div,记住创建Dijit模板时一定要有一个唯一的根节点。这里这个容器div就是我们模板的根节点。然后我们使用一个H3标签来展示作者名字,一个img标签来展示头像,一个p标签来展示作者的简介。

1
2
3
4
5
<div>
     <h3>Brian Arnold</h3>
     <img src= "./myApp/widget/images/avatar.jpg" >
     <p>Brian Arnold  is  a software engineer at SitePen, Inc., ...</p>
</div>


3、把HTML片段变成Dijit模板

    当使用dijit/_TemplatedMixin时,你对模板会有许多操作的方式:

1. 你可以在模板中自动插入变量值

2. 你自己在模板中定义附着点元素(attach point),这样你在widget中引用并编程操纵这些DOM元素

3. 可以在模板中的元素上设定DOM事件的处理函数

    在这个例子里,我们主要会用到自动的变量值插入。现在我们来创建文件: myApp/widget/templates/AuthorWidget.html,文件的内容基本和第二步中的HTML片段相似,只是会添加一些Dijit模板特有的属性。

1
2
3
4
5
<div>
     <h3 data-dojo-attach-point= "nameNode" >${name}</h3>
     <img  class = "${baseClass}Avatar"  src= ""  data-dojo-attach-point= "avatarNode" >
     <p data-dojo-attach-point= "bioNode" >${!bio}</p>
</div>

在这一模板中:

1. 我们使用 ${attribute}的语法来直接插入一些值,例如这里我们插入了作者姓名${name}

2. 类似的,还有一种语法是${!attribute}。这两者的区别是,${!attribute} 会对值原样插入而不做转义。在这里我们在${!bio}位置要替换的值会包含一些HTML标记,我们不希望dijit/_TemplatedMixin 对这些标记做转义。

3. 所有基于 dijit/_WidgetBase的小部件都有一个baseClass属性。 

4. 在上述模板中,我为每个节点都定义了附着点属性,这样在我的代码中就可以直接使用诸如myAuthor.nameNode这样的代码直接访问到H3节点。

    你也许已经注意到了我在模板中的img标签里没有指定src属性值。那么如果我们的数据里某个作者没有包含头像图片怎么办?我们需要能够在创建我们的小部件时自动设定处理这类情形的默认值。后面的步骤会进一步解释如何解决这个问题。


4、使用dojo/_base/declare创建小部件类

    这一步我们要在myApp/widget目录里创建AuthorWidget.js,并且我们添加一个默认的头像的图片文件。创建之后的目录和文件结构如下:

wKioL1RltGXhxrEOAABdEq-Zoi4989.jpg

    在AuthorWidget.js 里我们将使用dojo/_base/declare创建我们的小部件类,如下:

1
2
3
4
5
6
//custom/AuthorWidget.js
define([ "dojo/_base/declare" , "dijit/_WidgetBase" "dijit/_TemplatedMixin" ],
     function (declare, _WidgetBase, _TemplatedMixin){
         return  declare([_WidgetBase, _TemplatedMixin], {
     });
});

    当然,上面的代码还只是一个空架子,要让它真的可以工作,我们还需要给我们的小部件设置一系列属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
define([
     "dojo/_base/declare" ,
     "dijit/_WidgetBase" ,
     "dijit/_TemplatedMixin" ,
     "dojo/text!./templates/AuthorWidget.html" ,
     "dojo/dom-style" ,
     "dojo/_base/fx" ,
     "dojo/_base/lang" ,
     "dojo/on" ,
     "dojo/mouse" ,
     "require"  // context-sensitive require to get URLs to resources from relative paths
],  function (declare, _WidgetBase, _TemplatedMixin, template, domStyle, baseFx, lang, on, mouse, require){
     return  declare([_WidgetBase, _TemplatedMixin], {
         // 定义了一些属性用于保存作者的基本信息:姓名,简介和头像。设置这些属性的默认值
         name:  "No Name" ,
         //使用require.toUrl获取图像默认值
         avatar: require.toUrl( "./images/defaultAvatar.png" ),
         bio:  "" ,
         // 设置我们的模板 - important!
         templateString: template,
         // 将会被应用到模板根节点的css类名
         baseClass:  "authorWidget" ,
         // 指向我们背景动画对象的引用
         mouseAnim:  null ,
         // 用于背景的颜色属性
         baseBackgroundColor:  "#fff" ,
         mouseBackgroundColor:  "#def" ,
 
         // 添加postCreate方法
         postCreate:  function (){
             // 获取小部件的DOM节点
             var  domNode =  this .domNode;
             console.dir(domNode);
             // Run any parent postCreate processes - can be done at any point
             this .inherited(arguments);
             // 设置节点背景颜色
             domStyle. set (domNode,  "backgroundColor" this .baseBackgroundColor);
             // 设置节点的onmouseenter和onmouseleave的事件处理函数,目的改变节点背景颜色
             this .own(
                 on(domNode, mouse.enter, lang.hitch( this "_changeBackground" this .mouseBackgroundColor)),
                 on(domNode, mouse.leave, lang.hitch( this "_changeBackground" this .baseBackgroundColor))
             );
         },
 
         // 定制avatar属性方法
         // myWidget.set('avatar', someValue)
         _setAvatarAttr:  function (imagePath) {
             // 若图像值不为空,赋值
             if  (imagePath) {
                 this ._set( "avatar" , imagePath);
                 this .avatarNode.src = imagePath;
             }
         },
 
         _changeBackground:  function (newColor) {
             // 检查是否当前有动画正在执行,如果有的话我们先停止它
             if  ( this .mouseAnim) {
                 this .mouseAnim.stop();
             }
             // 设置新的动画并保存在mouseAnim中,并开始播放
             this .mouseAnim = baseFx.animateProperty({
                 node:  this .domNode,
                 properties: {
                     backgroundColor: newColor
                 },
                 onEnd: lang.hitch( this function () {
                     // Clean up our mouseAnim property
                     this .mouseAnim =  null ;
                 })
             }).play();
         }
 
     });
});

说明:

    (1)通过使用templateString属性,和dojo/text,我们指定模板的内容。

    (2)设置baseClass属性,该属性会被设置为小部件的DOM根节点的CSS类。在这里就是我们模板中的最外层div节点。

    (3)我们还留出了用于设置动画的属性和背景色的属性。 

    (4)添加postCreate方法;一个定制的头像设定setter方法;一个变换背景颜色函数。postCreate方法是用来添加我们的工作逻辑的主要入口。它的调用时机是在小部件的DOM结构成功创建后,但是还没有被添加到页面的DOM树之前(也即用户看不到这个小部件的DOM节点)。因此通常我们把初始化的工作放在这个方法中。

    (5)在postCreate里我们利用baseBackgroundColor属性设置了domNode的背景色,并且设置了 函数就会被调用。 

    (6)最后,我们需要解决一个问题,就是如果某个作者没有提供头像图片,我们需要给他设置一个默认的头像。 这里我们通过创建一个定制的属性设置方法(custom setter) 来实现。 这类方法有固定的命名规则, _setXXXAttr  其中XXX是你要设置的属性的名称(首字母需大写)。 例如我们这里要设置的是“avatar属性,因此我们需要的方法名是_setAvatarAttr。

    在小部件被创建或者用户使用属性设置方法:myWidget.set("avatar",somePath) 时,定制属性方法就会被调用。


5、美化

    使用dijit/_WidgetBase的好处之一,就是他提供了一个baseClass属性作为根节点的CSS样式类。之前创建的目录结构中有专门的css目录,接下来我们在css目录中创建一个AuthorWidget.css 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* AuthorWidget.css */
.authorWidget {
     border: 1px solid black;
     width: 400px;
     padding: 10px;
     overflow: hidden;  /* I hear this helps clear floats inside */
}
.authorWidget h3 {
     font-size:  1 .5em;
     font-style: italic;
     text-align: center;
     margin: 0px;
}
.authorWidgetAvatar {
     float: left;
     margin: 4px 12px 6px 0px;
     max-width: 75px;
     max-height: 75px;
}

    可以看到我们定义了authorWidget 类,这个类是baseClass所制定的,它会被应用到模板的div根节点上。 同时,模板中还有 ${baseClass}Avatar类,因此我们定义了authorWidgetAvatar类。

wKioL1RluxeBRUlBAAKtrLGG7ZM906.jpg













参考:

http://blog.csdn.net/dojotoolkit/article/details/6688058

http://www.ibm.com/developerworks/cn/web/wa-aj-dojotool/

http://www.ibm.com/developerworks/cn/websphere/techjournal/1209_weller/1209_weller.html