创建 基于模板的小部件(Widget)
在本教程中,你将学到Dijit 的重要组成部分模板系统:Dijit._Templated ,以及如何使用模板来快速创建自己的小部件
难度: 中等, 使用Dojo 版本: 1.6
Dijit (Dojo的小部件库)提供的_Widget 和_WidgetBase基类为开发小部件提供了完整的基础类,但是_Templated 基类的所引入的模板系统则使Dijit更显强大和灵活。
使用_Templated , 你可以快速的创建出易于维护,方便修改的小部件。
_Templated 的原理非常简单:它允许开发者创建一小段HTML片段,片段在运行时会被作为字符串加载(或者在build过程中直接内联入代码内)并被所有该小部件的实例所重用。
接下来我们将会解读_Templated 提供了哪些功能,并且动手开发一个使用_Templated 的小部件。
注意:_Templated 应当被当作混入代码使用(mixin), 而不是被直接继承。 用面向对象的观点来看,_Templated 更接近一个接口而不是一个类(虽然在JavaScript中,并没有办法明确区分二者)
对使用_Templated 的程序员来说或,将_Templated 混入一个小部件将会给这个小部件的类带来下列的新属性:
templateString, // a string representing the HTML of the template
templatePath, // a URL pointing at a file to be used as a template
widgetsInTemplate // a Boolean indicating whether or not child widgets
// are defined in the template
注意:templatePath 现在已经基本不使用了,只是为了向后兼容而保留。下面的章节中我们会展示如何搭配使用dojo.cache 和 templateString
这些新属性看起来如此简单,那么多强大的功能仅凭这几个属性就能实现么?真正的答案隐藏在_Templated向你的小部件定义中添加的其他内容。
除了上述几个属性之外, _Templated覆写了Dijit Widget架构中的三个基础方法:buildRendering, destroyRendering, 和startup. 这三个方法分别负责:解析并填充模板(buildRendering),正确销毁Widget DOM树(destroyRendering), 并保障模板中包含的子Widget能够正确的被启动(startup)
注意:因为上述三个方法对于使用模板是必须的,如果你在自己的代码中也进一步覆写了这几个方法,请务必在你的覆写代码中加入this.inherited(arguments) 来调用父类的方法确保模板能正确处理。
Dijit 组件库中的组件都遵循在当前Javascript目录中创建一个单独的名为templates的目录来保存模板的编码习惯。 我们建议你也遵循同样的做法。
注意:在上述声明中,我们搭配使用了dojo.cache 和templateString 属性,dojo.cache方法会从其缓存或url中获取资源。这是最新版dojo中推荐的用来获取资源(例如这里的模板文件)的做法,它确保了使用尽量少的HTTP请求来获取资源.
现在我们已经有了可以支持模板的一个小部件的声明,接下来我们要为它写一个模板,并且解释模板可以包含哪些功能。
一个模板就是一个HTML代码片段,你可以在其中定义DOM结构,也可以注入一些特殊的内容。 我们先从一个简单例子看起。
在这个简单的模板里,演示了Dijit模板的3个重要功能:变量的替换,附着点(attach point), 以及DOM事件绑定. 接下来我们会依次解释这三个功能。
注意:每个模板的HTML中只能有一个根节点。 包含多个根节点的模板是非法的。
模板中可以引用小部件中定义的变量的值。语法很简单
${property}
上述例子中,我们在模板的根节点的class属性里引用了变量baseClass的值(该变量适用于所有Widget). 如果该变量本身是一个对象的话,你还可以进一步引用该对象的属性值。 这时你需要在属性名前加上一个!
如:
${!propertyObject.property}
注意:从Dijit 1.5 开始,模板中使用变量值替换仅限用于哪些在小部件的整个生命周期中都不会变化的那些属性。如果你希望可以在声明周期中修改某属性值,我们建议你在postCreate方法中通过调用set方法来设置。
Dijit 模板系统会在你定义的模板中寻找一个特殊的属性(称为attach point) - 使用了HTML5 的数据属性(data-*) 语法。一个连接点告诉模板渲染引擎: 如果模板中的某DOM节点上定义了data-dojo-attach-point 属性,则该节点的引用会被设置为在Widget中的对应属性。例如, 在上面例子中的SomeWidget的模板 定义了2个DOM节点。主节点(外层的div)可以在Widget代码中被引用为focusNode,而内层的span节点则可以通过Widget的containerNode属性来引用。
注意: 通常即使你在模板中不设置任何attach point ,模板的根节点可以在widget中通过domNode来引用。所以大多数时候你用不着显示的使用这个属性。
Dijit定义了一个特殊的附着点叫做”containerNode" . 从名字可以理解,containerNode就是一个容器节点,它为使用声明(declarative)方式创建的widget提供了放置额外标记片段的地方。例如:
这个片段使用声明的方式创建了我们前面定义的SomeWidget的实例,在Dojo解释器解析这个段落时,它会实例化一个SomeWidget,而实例化的同时在这里被Div包含的HTML片段都会添加到SomeWidget所定义的容器节点中去。 所以当这个Widget被创建后,DOM树的结构会是:
注意:上例中为了教程的简洁我们省去了一些自定义HTML5属性。
类似的,如果你在Widget声明中嵌入其他的Widget,那么子Widget也将被添加到你的容器节点中去(前提是你的Widget定义了一个容器节点) . 例如:
除了附着点,Dijit 模板系统还允许将原生DOM事件绑定到Widget的自定义方法上。这是通过另一个HTML5 自定义数据属性完成的:data-dojo-attach-event 。 该属性值是一组由逗号分割的键值对(键值间用冒号分开),键是原生DOM事件,值是要绑定此事件的Widget中的自定义方法。
例如:
这个例子里我们定义的ondijitclick事件是一个Dijit自己定义的稍有改动onclick处理方法,在Dijit里通常可以使用onclick 的地方都可以是用ondijitclick 代替。
当Widget被实例化时,Diji模板系统会自动遍历所有的事件绑定属性,并使用connect 将这些事件和Widget中的方法绑定起来。当DOM事件被触发时,你的事件处理函数将能获得跟原生DOM事件同样多的参数,因此你可以完全的控制你的Widget.
最后,Dijit模板系统允许你使用widgetsInTemplate属性来通过模板创建组合控件。顾名思义,widgetsInTemplate 指明了你的模板中是否包含其他的Widget(默认值为false).
让我们对我们前面的例子稍作修改,来让它始终包含一个dijit.button
请注意在修改后的模板中,我们在dijit.form.Button的div节点上定义了一个连接点buttonWidget. 此后我们可以直接使用myWidget.buttonWidget来直接引用这个内嵌的button的widget (而不是仅仅引用这个DOM节点). 使用这种方法,你可以使用一些简单的widget来构建组合的widget,例如一个可以查看邮件列表的widget,或者是一个包含一系列按钮的工具条控件。
注意:使用widgetsInTemplate 时可能会有额外的开销,可能会影响你的widget的性能,甚至整个页面的性能。如果你不是非常清楚的了解这个属性的作用,最好还是将它设为false.
在这篇教程中,我们学习了Dijit 强大的模板系统,使用混入 _Templated,以及你如何使用模板来快速构建自定义的Widget
. 我们还介绍了如何在模板中使用附着点,如何绑定DOM事件,以及如何构建包含其他widget的组合Widget的方法。
作者:Tom Trenka
原文链接:http://dojotoolkit.org/documentation/tutorials/1.6/templated/
译者: feijia (tiimfei@gmail.com)
在本教程中,你将学到Dijit 的重要组成部分模板系统:Dijit._Templated ,以及如何使用模板来快速创建自己的小部件
难度: 中等, 使用Dojo 版本: 1.6
Dijit (Dojo的小部件库)提供的_Widget 和_WidgetBase基类为开发小部件提供了完整的基础类,但是_Templated 基类的所引入的模板系统则使Dijit更显强大和灵活。
使用_Templated , 你可以快速的创建出易于维护,方便修改的小部件。
_Templated 的原理非常简单:它允许开发者创建一小段HTML片段,片段在运行时会被作为字符串加载(或者在build过程中直接内联入代码内)并被所有该小部件的实例所重用。
接下来我们将会解读_Templated 提供了哪些功能,并且动手开发一个使用_Templated 的小部件。
注意:_Templated 应当被当作混入代码使用(mixin), 而不是被直接继承。 用面向对象的观点来看,_Templated 更接近一个接口而不是一个类(虽然在JavaScript中,并没有办法明确区分二者)
_Templated 提供了哪些方法和属性
对使用_Templated 的程序员来说或,将_Templated 混入一个小部件将会给这个小部件的类带来下列的新属性:
templateString, // a string representing the HTML of the template
templatePath, // a URL pointing at a file to be used as a template
widgetsInTemplate // a Boolean indicating whether or not child widgets
// are defined in the template
注意:templatePath 现在已经基本不使用了,只是为了向后兼容而保留。下面的章节中我们会展示如何搭配使用dojo.cache 和 templateString
这些新属性看起来如此简单,那么多强大的功能仅凭这几个属性就能实现么?真正的答案隐藏在_Templated向你的小部件定义中添加的其他内容。
_Templated覆写的方法
除了上述几个属性之外, _Templated覆写了Dijit Widget架构中的三个基础方法:buildRendering, destroyRendering, 和startup. 这三个方法分别负责:解析并填充模板(buildRendering),正确销毁Widget DOM树(destroyRendering), 并保障模板中包含的子Widget能够正确的被启动(startup)
注意:因为上述三个方法对于使用模板是必须的,如果你在自己的代码中也进一步覆写了这几个方法,请务必在你的覆写代码中加入this.inherited(arguments) 来调用父类的方法确保模板能正确处理。
使用_Templated
你只需要在创建Widget的类声明中的第二个参数数组中加入“dijit._Templated" ,就可以让你的Widget 支持模板. 例如,下面的代码就声明了一个支持模板的名为SomeWidget的Widget
dojo.declare("example.SomeWidget", [ dijit._Widget, dijit._Templated ], {
templateString: dojo.cache("example", "templates/SomeWidget.html")
// your custom code goes here
});
Dijit 组件库中的组件都遵循在当前Javascript目录中创建一个单独的名为templates的目录来保存模板的编码习惯。 我们建议你也遵循同样的做法。
注意:在上述声明中,我们搭配使用了dojo.cache 和templateString 属性,dojo.cache方法会从其缓存或url中获取资源。这是最新版dojo中推荐的用来获取资源(例如这里的模板文件)的做法,它确保了使用尽量少的HTTP请求来获取资源.
现在我们已经有了可以支持模板的一个小部件的声明,接下来我们要为它写一个模板,并且解释模板可以包含哪些功能。
创建模板
一个模板就是一个HTML代码片段,你可以在其中定义DOM结构,也可以注入一些特殊的内容。 我们先从一个简单例子看起。
<div class="${baseClass}" data-dojo-attach-point="focusNode" data-dojo-attach-event="ondijitclick:_onClick" role="menuitem" tabindex="-1">
<span data-dojo-attach-point="containerNode"></span>
</div>
在这个简单的模板里,演示了Dijit模板的3个重要功能:变量的替换,附着点(attach point), 以及DOM事件绑定. 接下来我们会依次解释这三个功能。
注意:每个模板的HTML中只能有一个根节点。 包含多个根节点的模板是非法的。
变量的替换:
模板中可以引用小部件中定义的变量的值。语法很简单
${property}
上述例子中,我们在模板的根节点的class属性里引用了变量baseClass的值(该变量适用于所有Widget). 如果该变量本身是一个对象的话,你还可以进一步引用该对象的属性值。 这时你需要在属性名前加上一个!
如:
${!propertyObject.property}
注意:从Dijit 1.5 开始,模板中使用变量值替换仅限用于哪些在小部件的整个生命周期中都不会变化的那些属性。如果你希望可以在声明周期中修改某属性值,我们建议你在postCreate方法中通过调用set方法来设置。
附着点(Attach Points)
Dijit 模板系统会在你定义的模板中寻找一个特殊的属性(称为attach point) - 使用了HTML5 的数据属性(data-*) 语法。一个连接点告诉模板渲染引擎: 如果模板中的某DOM节点上定义了data-dojo-attach-point 属性,则该节点的引用会被设置为在Widget中的对应属性。例如, 在上面例子中的SomeWidget的模板 定义了2个DOM节点。主节点(外层的div)可以在Widget代码中被引用为focusNode,而内层的span节点则可以通过Widget的containerNode属性来引用。
注意: 通常即使你在模板中不设置任何attach point ,模板的根节点可以在widget中通过domNode来引用。所以大多数时候你用不着显示的使用这个属性。
containerNode 容器节点
Dijit定义了一个特殊的附着点叫做”containerNode" . 从名字可以理解,containerNode就是一个容器节点,它为使用声明(declarative)方式创建的widget提供了放置额外标记片段的地方。例如:
<div data-dojo-type="example.SomeWidget">
<p>This is arbitrary content!</p>
<p>More arbitrary content!</p>
</div>
这个片段使用声明的方式创建了我们前面定义的SomeWidget的实例,在Dojo解释器解析这个段落时,它会实例化一个SomeWidget,而实例化的同时在这里被Div包含的HTML片段都会添加到SomeWidget所定义的容器节点中去。 所以当这个Widget被创建后,DOM树的结构会是:
<div class="someClass" role="menuitem" tabindex="-1">
<span>
<p>This is arbitrary content!</p>
<p>More arbitrary content!</p>
</span>
</div>
注意:上例中为了教程的简洁我们省去了一些自定义HTML5属性。
类似的,如果你在Widget声明中嵌入其他的Widget,那么子Widget也将被添加到你的容器节点中去(前提是你的Widget定义了一个容器节点) . 例如:
<div data-dojo-type="example.SomeWidget">
<p>This is arbitrary content!</p>
<div data-dojo-type="dijit.form.Button">My Button</div>
<p>More arbitrary content!</p>
</div>
事件绑定
除了附着点,Dijit 模板系统还允许将原生DOM事件绑定到Widget的自定义方法上。这是通过另一个HTML5 自定义数据属性完成的:data-dojo-attach-event 。 该属性值是一组由逗号分割的键值对(键值间用冒号分开),键是原生DOM事件,值是要绑定此事件的Widget中的自定义方法。
例如:
data-dojo-attach-event="onmouseenter:_onHover,onmouseleave:_onUnhover,ondijitclick:_onClick"
这个例子里我们定义的ondijitclick事件是一个Dijit自己定义的稍有改动onclick处理方法,在Dijit里通常可以使用onclick 的地方都可以是用ondijitclick 代替。
当Widget被实例化时,Diji模板系统会自动遍历所有的事件绑定属性,并使用connect 将这些事件和Widget中的方法绑定起来。当DOM事件被触发时,你的事件处理函数将能获得跟原生DOM事件同样多的参数,因此你可以完全的控制你的Widget.
widgetsInTemplate 属性
最后,Dijit模板系统允许你使用widgetsInTemplate属性来通过模板创建组合控件。顾名思义,widgetsInTemplate 指明了你的模板中是否包含其他的Widget(默认值为false).
让我们对我们前面的例子稍作修改,来让它始终包含一个dijit.button
// 修改后的Widget定义
dojo.declare("example.SomeWidget", [ dijit._Widget, dijit._Templated ], {
templateString: dojo.cache("example", "templates/SomeWidget.html"),
widgetsInTemplate: true
// 你的代码
});
// 模板定义
<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>
请注意在修改后的模板中,我们在dijit.form.Button的div节点上定义了一个连接点buttonWidget. 此后我们可以直接使用myWidget.buttonWidget来直接引用这个内嵌的button的widget (而不是仅仅引用这个DOM节点). 使用这种方法,你可以使用一些简单的widget来构建组合的widget,例如一个可以查看邮件列表的widget,或者是一个包含一系列按钮的工具条控件。
注意:使用widgetsInTemplate 时可能会有额外的开销,可能会影响你的widget的性能,甚至整个页面的性能。如果你不是非常清楚的了解这个属性的作用,最好还是将它设为false.
结论
在这篇教程中,我们学习了Dijit 强大的模板系统,使用混入 _Templated,以及你如何使用模板来快速构建自定义的Widget
. 我们还介绍了如何在模板中使用附着点,如何绑定DOM事件,以及如何构建包含其他widget的组合Widget的方法。