创建基于模版的Widgets

这个教程中,你将学习重要的Dijit's _TemplatedMixin混入,以及如何快速创建你自己的自定义widgets。


开始准备

Dijit's _WidgetBase提供了强有力的创建widgets基础,但是_TemplatedMinxin混入是Dijit真正的闪关点。使用_TemplatedMixin和_WidgetsInTemplateMixin,你可以快速的创建高度可维护、快速修改和操作简单的widgets。


_TemplatedMixin基本的概念很简单:它允许开发人员创建一个小的HTML文件,里面有一些小的扩展,并在运行时加载该HTML文件作为一个字符串(或在构建过程中内联)为所有实例提供可重用的模板化widget。


让我们先了解_TemplatedMixin的定义(和原因),然后从头开始使用它的函数建立一个小的widget。


 注意_TemplatedMixin是作为混入使用的,而不是直接继承。在class-based(基于类)的说法中,这意味着它相对于class(类)更像一个interface(接口)(尽管在JavaScript中两者的不同很模糊)。查看Dojo Declare Tutorial 获得更多如何在Dojo中使用类的信息。


_TemplatedMixin提供了什么?

对于开发人员的工作,_TemplatedMixin混合进widget的定义中,提供了你的widget以下的附加属性:

templateString      //  a string representing the HTML of the template

这个特性看似简单 —— 毕竟,这么小点东西能有多大的能力?答案在于_TemplatedMixin添加其他到你的widget定义中。


 一小点要注意:templatePath也会添加,但是它不再用于模版载入,它仍然为了让后兼容。稍后介绍怎么使用dojo/text!去载入widget模版。


重写方法

除了上面的特性,_TemplatedMixin重写了两个定义在Dijit's widget构架中的方法:buildRendering 和 destroyRendering。这两个方法用来解析和填充模版(buildRendering)和正确的销毁widget's DOM(destroyRendering)。


 因为这是模版中关键的两个方法,如果你要重写任何一个在你的自拟定代码中 —— 确保在你的重写方法中调用this.inherited(arguments)来执行父类的方法。查看 Understanding _WidgetBase Tutorial 获取更多关于widget生命周期的信息。


使用_TemplatedMixin

为了使你的自定义widget“模板化",你需要为你的widget类声明数组中添加dijit/_TemplatedMixin作为第二个或者随后的参数。例如,可能像这样去声明一个SomeWidget模版的widget:

1
2
3
4
5
6
7
8
9
10
11
12
13
define([
     "dojo/_base/declare" ,
     "dijit/_WidgetBase" ,
     "dijit/_TemplatedMixin" ,
     "dojo/text!./templates/SomeWidget.html"
],  function (declare, _WidgetBase, _TemplatedMixin, template) {
                                              
     return  declare([_WidgetBase, _TemplatedMixin], {
         templateString: template
         //  your custom code goes here
     });
                                              
});

 Dijit遵循创建一个templates文件夹在同一层次目录中用于JavaScript声明的标准,我们建议你也这样做。


注意在上面我们基本的声明中,我们通过dojo/text!{path}结合一个模版加载使用了templateString特性,推荐使用这个方式建立一个模版文件引用,因为它确保一个文件可以被异步加载和正确的整体在创建Dojo Toolkit构架中。


现在,我们已经设置了基于模版的widget,让我们编写一个模版并谈论它们一些特殊的可用的钩子。


编写模版

一个模版是一个定义DOM结构的HTML文档片段,随着任何特殊钩子带回widget声明中。让我们看一个简单的例子在深入每个钩子之前,和多少变量被替换在模版中。这里有一个假设的模版用于我们的SomeWidget:

1
2
3
4
< div  class = "${baseClass}" >
     < div  class = "${baseClass}Title"  data-dojo-attach-point = "titleNode"
             data-dojo-attach-event = "onclick:_onClick" > div >
div >

虽然简单,这个模板演示了三个最重要的Dijit模板系统的方面:变量替换、附着点和事件附着物。


注意,当您定义一个模板,它只能有一个根节点定义(就像XML文档)。多个节点在顶层是不允许的。


变量替换

一个模版可以有值设置在DOM上,通过使用简单的变量占位符语法,看起来就像:

1
${property}

变量的名字可以是任何特性或字段定义在widget声明中。上面的示例中使用的是特性baseClass(任何widget中都有效),但是自定义字段使用更简洁——例如,如果我定义一个属性叫foo在我们的SomeWidget里,我们只要简单在模版中使用${foo}。如果属性是一个对象的引用,而且你想使用这个对象中属性的值,你可以非常容易的通过普通的对象引用符号:

1
${propertyObject.property}

为了防止_TemplatedMixin溢出问题在字符串里,放置一个"!"在变量名前,像:

1
${!property}

 在模版中被替换的变量推荐使用widget寿命内一直不变的值。换句话说,如果你希望在widget寿命内设置属性的值,我们建议使用widget的postCreate()方法设置所有变量在程序中,而不是使用widget的set()方法。


附着点

Dijit的模版系统有一个特殊的叫attach point的属性,它可以在你的模版内查找——使用HTML5数据属性语法实现。attach point告诉模版当带有data-dojo-attach-point属性定义的DOM元素被创建时进行渲染,设置这个属性的值将作为widget的一个特性,用于引用被创建的DOM元素。例如,SomeWidget的模版中定义了两个DOM元素,主元素(最外层的div)可以通过domNode特性在代码里引用,内层的div元素可以通过titleNode引用。


 通常模版的根节点将作为widget的domNode特性,所以你不需要定义attach point属性,然而,有时候这样做可以允许根节点作为其他子系统,例如Dijit的焦点管理。


The containerNode Attach Point

Dijit也定义了一个“神奇”附着点叫containerNode。这个containerNode的基本概念是:如果一个widget是用声明方式创建的,它为任何附加的标签提供一些空间。例如,设置SomeWidget的模版:

1
2
3
4
5
6
7
< div  class = "${baseClass}" >
     < div  class = "${baseClass}Title"  data-dojo-attach-point = "titleNode"
             data-dojo-attach-event = "ondijitclick:_onClick" > div >
     < div >And our container: div >
     < div  class = "${baseClass}Container"
             data-dojo-attach-point = "containerNode" > div >
div >

我们在标签声明中使用这个模版:

1
2
3
4
5
< div  data-dojo-type = "demo/SomeWidget"
         data-dojo-props = "title: 'Our Some Widget'" >
     < p >This is arbitrary content! p >
     < p >More arbitrary content! p >
div >

当dojo parser遍历文档,它将找到我们的例子中的widget并实例化——任何widget中的标签将被附加到containerNode中,作为实例的一部分。因此,当widget启动完成,DOM的结果应该像这样:

1
2
3
4
5
6
7
< div  id = "demo_SomeWidget_0"  class = "someWidgetBase" >
     < div  class = "someWidgetTitle" >Our Some Widget div >
     < div  class = "someWidgetContainer" >
         < p >This is arbitrary content! p >
         < p >More arbitrary content! p >
     div >
div >

注意,我们删除了一些自定义属性为简洁起见。


同样要主要如果你在主标签中插入其他widget定义,任何widget将实例化在包含节点内。例如:

1
2
3
4
5
< div  data-dojo-type = "demo/SomeWidget" >
     < p >This is arbitrary content! p >
     < div  data-dojo-type = "dijit/form/Button" >My Button div >
     < p >More arbitrary content! p >
div >

事件附着物

除了附着点,Dijit模版系统提供了一个在你的自定义widget中添加原生DOM事件方法的方式。它通过使用HTML5数据属性data-dojo-attach-event实现。它使用逗号分开每对key/value(冒号分隔)。key是原生DOM事件,用于附着处理函数。value是你的widget的方法名称,当事件触发时执行。如果只有一个事件,忽略尾部的冒号。例如:

1
data-dojo-attach-event="onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick"

当你的widget被实例化和模版内的DOM片段被创建,Dijit模版系统将通过任何附加的事件定义自动连接这些事件(使用dojo/on)。此外,当这些事件处理函数触发,同样的参数通常通过原生DOM事件机制,将被传递到你的widget的处理函数,以便你可以完全访问浏览器的消息报告。


同样,我们需要使用 dijit/_OnDijitClickMixin,它是一个修改过的事件相对标准DOM click事件增加了更多的功能。因此我们需要修改widget的定义:

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
//demo/SomeWidget.js
 
define([
     "dojo/_base/declare" ,
     "dijit/_WidgetBase" ,
     "dijit/_OnDijitClickMixin" ,
     "dijit/_TemplatedMixin" ,
     "dojo/text!./templates/SomeWidget.html"
], function (declare, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin, template){
  
     return  declare([_WidgetBase, _OnDijitClickMixin, _TemplatedMixin], {
         //  set our template
         templateString: template,
  
         //  some properties
         baseClass:  "someWidget" ,
         title:  "" ,   //  we'll set this from the widget def
  
         //  hidden counter
         _counter: 1,
         _firstClicked:  false ,
  
         //  define an onClick handler
         _onClick:  function (){
             if ( this ._firstClicked){
                 this .titleNode.innerHTML =  this .title +  " was clicked "  + (++ this ._counter) +  " times." ;
             else  {
                 this .titleNode.innerHTML =  this .title +  " was clicked!" ;
                 this ._firstClicked =  true ;
             }
         },
  
         postCreate:  function (){
             this .titleNode.innerHTML =  this .title;
         }
     });
});

我们同样要修改widget模版:

1
2
3
4
5
6
7
8
< div  class = "${baseClass}" >
     < div  class = "${baseClass}Title"
         data-dojo-attach-point = "titleNode"
         data-dojo-attach-event = "ondijitclick:_onClick" > div >
     < div >And our container: div >
     < div  class = "${baseClass}Container"
         data-dojo-attach-point = "containerNode" > div >
div >

查看演示


_WidgetsInTemplateMixin混入类

通过使用_WidgetsInTemplateMixin混入类,Dijit模版系统允许你从模版中创建更复杂的widget。这个混入类告诉模版系统你的模版里有其他的widget,并且在widget实例化的时候实例化它们。


例如,让我们修改定义包含一个Dijit按钮:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
define([
     "dojo/_base/declare" ,
     "dijit/_WidgetBase" ,
     "dijit/_OnDijitClickMixin" ,
     "dijit/_TemplatedMixin" ,
     "dijit/_WidgetsInTemplateMixin" ,
     "dijit/form/Button" ,
     "dojo/text!./templates/SomeWidget.html"
],  function (declare, _WidgetBase, _OnDijitClickMixin, _TemplatedMixin,
             _WidgetsInTemplateMixin, Button, template) {
   
     return  declare( "example.SomeWidget" , [_WidgetBase, _OnDijitClickMixin,
         _TemplatedMixin, _WidgetsInTemplateMixin
     ], {
         templateString: template
         //  your custom code goes here
     });
   
});

接着创建模版像这样:

1
2
3
4
5
6
7
8
9
< div  class = "${baseClass}"  data-dojo-attach-point = "focusNode"
         data-dojo-attach-event = "ondijitclick:_onClick"
         role = "menuitem"  tabIndex = "-1" >
     < div  data-dojo-type = "dijit/form/Button"
         data-dojo-attach-point = "buttonWidget" >
         My Button
     div >
     < span  data-dojo-attach-point = "containerNode" > span >
div >

注意在修改过的模版中,我们添加了一个包含buttonWidget附着点的按钮标签。这是附着点系统额外的奖励,由于它的定义是一个widget,添加widget的特性——myWidget.buttonWidget——将引用实际的按钮widget,而不是引用DOM元素。这允许创建超越简单建筑块的"超级widget",例如一个显示邮件的列表的widget中,添加工具栏或者更多widget。


同时注意,你应该在模块中引用所有模版中用到的widget。你不能充分利用Dojo 1.8中引入的dojo/parser"自动引用"特性,因为创建过程是同步运行的,但是自动引用特性是异步的。


不要混入dijit/_WidgetsinTemplateMixin这个类,除非你明确的需要定义widget。如果过度使用,这个额外的开销会影响widget和程序的性能。


结论

在这个教程里,通过混入_TemplatedMixin 和 _WidgetsInTemplateMixin我们学习了Dijit强大的模版系统,和如何利用这个系统快速创建自定义的widget。我们回顾一下主要的内容有:模板系统的附加点和事件附着物如何允许您快速绑定DOM元素到你的代码,如何在你的模板中替换值——以及如何在你的widget模板中包含其他widget来创建“超级widget”。

你可能感兴趣的:(dojo)