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

开始准备

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

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

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

ios 开发 widget 网络请求数据 widgetdmith_HTML 注意_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定义中。

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

重写方法

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

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

使用_TemplatedMixin

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

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         
                    });         
                    
          });

ios 开发 widget 网络请求数据 widgetdmith_HTML Dijit遵循创建一个templates文件夹在同一层次目录中用于JavaScript声明的标准,我们建议你也这样做。

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

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

编写模版

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

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

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

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

变量替换

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

${property}

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

${propertyObject.property}

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

${!property}

ios 开发 widget 网络请求数据 widgetdmith_HTML 在模版中被替换的变量推荐使用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引用。

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

The containerNode Attach Point

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

<          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          >

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

<          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的结果应该像这样:

<          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          >

ios 开发 widget 网络请求数据 widgetdmith_HTML注意,我们删除了一些自定义属性为简洁起见。

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

<          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的方法名称,当事件触发时执行。如果只有一个事件,忽略尾部的冒号。例如:

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

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

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

//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模版:

<          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按钮:

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         
                    });         
                    
          });

接着创建模版像这样:

<          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"自动引用"特性,因为创建过程是同步运行的,但是自动引用特性是异步的。

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

结论

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