这个教程中,你将学习重要的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以下的附加属性:
这个特性看似简单 —— 毕竟,这么小点东西能有多大的能力?答案在于_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”。