创建自定义微件

创建自定义微件

Creating a custom widget

In this recipe, we'll be covering how to leverage pieces of Dojo and the Dijit framework to create your own custom widgets, specifically covering use of dijit/_WidgetBase and dijit/_TemplatedMixin to quickly and easily set up your widget.

在本教程中,我们将会涉及如何使用Dojo和dijit框架构建自定义的微件,重点介绍使用 dijit/_WidgetBase 和 dijit/_TemplatedMixin 快速简便的构建你的微件。

Introduction

介绍

The Dojo Toolkit ships with the Dijit framework, which is a set of graphical controls called widgets. We can build graphical user interfaces with these widgets.

Dijit框架是Dojo中的重要模块,包含一系列的被称为微件的图形控件。我们通过这些微件来创建图形用户界面。

You may require a specific widget that is not provided by Dojo. In this case, you can use Dijit's core in order to build this widget with more ease.

你可能需要一些特定的微件,这些微件可能Dojo中没有包含。这种情况,你可以使用dojo Core 来创建这个模块。

Setup

设置

For our scenario, let's say that we have a data source somewhere, in JSON format, listing a series of authors, such as those who've penned a tutorial for Dojo. We happen to have that, and it looks something like this:

对于本案例,比方我们有一个数据源,它是JSON格式的,内容包含作者清单,例如为Dojo写教程的作者清单。我们刚好有这些,这个数据源看起来像下面这样:

[
    {
        "name": "Brian Arnold",
        "avatar": "/includes/authors/brian_arnold/avatar.jpg",
        "bio": "Brian Arnold is a software engineer at SitePen, Inc., ..."
    },
    /* More authors here... */
]

We also know that we want our end result to live in a page, somewhere like this:

我们也知道我们希望最终的结果呈现在一个页面中,像下面的这样:

<body>
    <!-- Headers and whatnot -->
    <h2>Authors</h2>
    <div id="authorContainer">
        <!-- Authors go here! -->
    </div>
</body>

We'll also say that we want it to be a little fancy—perhaps we get a background color to fade in as we mouse over it. Eventually, we want it to look something like this:

我们想让结果更有趣一些-比方当鼠标滑过时我们可以呈现一个背景色。最终,我们希望结果看起来像下面这样:

Solution

解决方案

We can create our own widget by following a simple series of steps.

我们可以按照如下的步骤来创建我们自己的微件:

  1. Create some file structure for our custom widget 为自定义微件创建文件框架
  2. Create markup that will represent an individual author创建显示单个作者的标签
  3. Augment our author markup to make it a Dijit template 为作者签添加变量使之成为一个Dijit模板
  4. Create our widget class using declare 使用declare 创建微件
  5. Style as appropriate 添加样式美化
Step 1: Create a file structure for our custom widget
第一步:为微件创建一个文件结构

While this step is arguably optional, it's generally considered a good practice to have a proper file structure for your custom Dijit work (or custom code in general). In this case "myApp" is the folder that will house all of our "custom" code — by "custom" we mean code written specifically for this app. General-purpose and 3rd-party libraries (like dojo, dijit, etc.) would be in folders that are siblings of "myApp". This name is completely up to you, but use something meaningful, like the name of your organization, or the application that this widget will be a part of. We like to group our widgets together, so we'll create a folder named "widget" directly under "myApp". We'll call our new widget AuthorWidget — it's module id will be myApp/widget/AuthorWidget. Widgets often use external resources, so we'll add some folders under the "widget" folder to organize them — css, images, and templates. Our eventual structure looks like this:

这个步骤是可选的,不过在微件中包含一个合适的文件来描述自定义Dijit微件是一种好的编程实践。在本例中,“myApp”文件夹包含所有的“自定义”代码-“自定义”指为本app所写的代码。基础模块和第三方开发包(例如dojo、dijit等)位于myApp文件夹的同级目录中。这个文件夹的名字由你自己决定,请使用一个有意义的名字,例如你的组织的名字,或者微件所在程序的名字。我们喜欢把我们的微件组织到一块,因此会在myApp目录下创建一个“widget”文件夹。我们将我们的新微件命名为AuthorWidget-模块名为“myApp/widget/AuthorWidget”。微件经常使用外部资源,因此我们在widget文件夹下添加一个文件夹来管理这些资源-css、图片、模板。最终的结构如下:

We haven't actually created any files yet in our custom space - just some hierarchy.

到目前为止我们只是创建了文件夹层级,还没有为微件添加任何文件。

Step 2: Create markup that will represent an individual author
第二步:创建展示一个作者信息的标签

Now that we have some structure to store our pieces, let's create some simple markup that represents an individual author. For your first widget, it's likely going to be simplest to just set up a basic page where you directly put in some sample values.

现在已经有了文件夹,我们来创建一个简单的标签展示单个作者信息。对于我们的第一个微件,最好能简单到只建立一个基本的页面,只需添加简单的数据。

When you're working out a template, you should always create one parent wrapping element that contains all of the other elements. This element can be whatever you want, but it's important to have just one root element. For our data, we'll use a div as our wrapping element. We'll put in our author's name using an H3 element, the image using an img element, and then our bio inside of a p element.

当规划一个模板时,你必须创建一个顶层包装元素以包含所有其他元素。这个元素可以是任意的你希望的,保证只有一个跟根元素即可。对于我们的数据,我们使用一个div作为根标签。我们使用H3标签包含作者名,使用image包含作者图片,使用p包含作者的简历。

<div>
    <h3>Brian Arnold</h3>
    <img src="/includes/authors/brian_arnold/avatar.jpg">
    <p>Brian Arnold is a software engineer at SitePen, Inc., ...</p>
</div>
Step 3: Augment our author markup to make it a Dijit template
第三步:扩展作者标签使之成为一个模板

When using dijit/_TemplatedMixin, you can adjust your markup in a variety of ways:

使用dijit/_TemplatedMixin,你有多种方式来调整标签。

  • You can have values from your widget automatically inserted
  • 你可以让微件中的值自定填充
  • You can designate elements in your template as Attach Points, giving you a programmatic reference to that node in the widget
  • 你可以指定模板中的元素为Attach Point,这样就可以微件中的元素增加一个程序访问入口。
  • You can set up methods to be called on DOM events related to specific nodes
  • 你可以为指定的节点配置dom事件响应方法。

For our purposes, we're not worried about events right now — but we definitely want to take advantage of some of the automatic insertion. We're going to create a file in our hierarchy, under myApp/widget/templates/ named AuthorWidget.html. It's basically the markup defined above, but with some simple additions.

目前来说,我们还不关心事件,-我们现在仅关心自动注入。我们现在要在上面的文件目录myApp/widget/templates/下创建文件AuthorWidget.html。它基本上是上面定义的标签,不过增加了一些简单的内容。

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

There are a few things to note as to what's going on here:

有几点需要注意:

  • We can use a ${attribute} syntax to directly insert some values, like our name.
  • 我们可以使用${attribute}语法来直接插入数据,例如本例中作者名字。
  • We can use a ${!attribute} syntax to directly insert some values from our widget as well, like we're doing with bio. The major distinction between ${attribute} and ${!attribute} in this case is that our bio contains HTML, and we want to avoid having dijit/_TemplatedMixin perform automatic escaping on the inserted content.
  • 我们也可以使用${!attribute}语法在微件中来插入某些值,像插入简历信息那样。在本例中,两者最大的区别在于,简历中包含HTML标签。我们想避免dijit/_TemplatedMixin对插入的内容执行自动转义。
  • All dijit/_WidgetBase-based widgets have a baseClass property by default, and by leaning on that, we can provide a custom class to our avatar.
  • 默认情况下,所有的基于dijit/_WidgetBase的微件都有一个baseClass属性,基于此,我们可以为我们的微件提供一个自定义类。
  • We've given all nodes an attach point, meaning that in our widget code, we can use that name to reference that node directly. Think of it kind of like doing some getElementById type work without needing IDs, where we set up references in advance — so with an instance of AuthorWidget, we could use myAuthor.nameNode to directly reference the H3 DOM node for that widget.
  • 每个节点我们都添加了一个attach point属性,这意味着在我们的微件代码中,我们可以使用这个名称查找到该节点。这类似于getElementById,不需要提供Id,我们提前设置在那儿设置引用,这样有了AuthorWidget的实例,我们就可以使用myAuthor.nameNode来直接获取微件的H3 Dom节点。

You might have noticed that we haven't set the avatar's source directly. What happens if we have an author that doesn't have an avatar specified? We don't want to show a broken image. We'll handle the default value for that when we create our widget, which we'll do now!

你可能已经注意到我们没有为头像设置图片源。如果一个作者没有设置头像会出现什么情况?我们不想呈现一个没有图片情况下的破碎图片。我们将会在创建微件时设置头像的默认图片,现在我们就开始做。

Step 4: Create our widget class using dojo/_base/declare
第四步:使用dojo/_base/declare创建微件类

At this point, in our file structure above, we're going to create a file named AuthorWidget.js in the widget folder. We'll also add a default avatar image. Our file structure is starting to fill out a little! After this step, it'll be much more filled out. We'll be doing the bulk of our work at this point.

现在,我们将要之前的目录结构的widget文件夹中创建一个名为AuthorWidget.js的文件,我们还将添加一个默认图标。我们的文件框架开始完善。在本步骤完成后,会更加完善。大部分工作都会在本步完成。

Now we can simply build our widget! The following code would go in your AuthorWidget.js file.

现在我们可以简单地构建我们的微件!以下代码需要添加到AuthorWidget.js文件中。

// myApp/widget/AuthorWidget.js
define(["dojo/_base/declare","dijit/_WidgetBase", "dijit/_TemplatedMixin"],
    function(declare, _WidgetBase, _TemplatedMixin){
        return declare([_WidgetBase, _TemplatedMixin], {
        });
}); // and that's it!

Using declare we easily create our custom AuthorWidget from dijit/_WidgetBase and dijit/_TemplatedMixin. Now, if we stopped here, this wouldn't work. We need to add a few custom properties for our widget, as well as set up some default values for properties left unspecified in the options passed when the widget is instantiated. Here's the setup for our declaration, now with properties.

使用declare我们方便的创建了基于dijit/_WidgetBase 、 dijit/_TemplatedMixin的自定义的AuthorWidget。现在,如果不继续添加代码的话,还没办法运行。我们需要为我们的微件添加一些自定义的属性,并为属性配置默认值,一些未设置的选项在微件初始化sh 。以下就是我们的声明,现在它已经赠加了一些属性。

define([
    "dojo/_base/declare",
    "dojo/_base/fx",
    "dojo/_base/lang",
    "dojo/dom-style",
    "dojo/mouse",
    "dojo/on",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/text!./templates/AuthorWidget.html"
], function(declare, baseFx, lang, domStyle, mouse, on, _WidgetBase, _TemplatedMixin, template){
    return declare([_WidgetBase, _TemplatedMixin], {
        // Some default values for our author一些author的默认值
        // These typically map to whatever you're passing to the constructor这通常映射到你传递给构造函数的值
        name: "No Name",
        // Using require.toUrl, we can get a path to our AuthorWidget's space require.toUrl定位到AuthorWidget所在目录
        // and we want to have a default avatar, just in case以防万一,我们设置一个默认头像
        avatar: require.toUrl("./images/defaultAvatar.png"),
        bio: "",

        // Our template - important!我们的模板-重要!
        templateString: template,

        // A class to be applied to the root node in our template 应用于末班中根节点的class
        baseClass: "authorWidget",

        // A reference to our background animation 背景动画变量
        mouseAnim: null,

        // Colors for our background animation 背景动画颜色
        baseBackgroundColor: "#fff",
        mouseBackgroundColor: "#def"
    });
});

We have several things going on here, so let's break it down.

我们这里有几点要说明。

  • We start off with some properties relevant to our author - namebioavatar - setting default values. By using require.toUrl, we can get a path to where our AuthorWidget is located, and tap into the images folder under there.
  • 我们开始为作者的几个属性-name、bio、avatar设置了默认值。使用require.toUrl,我们可以获取到AuthorWidget所在的目录,并通过此定位到图片目录。
  • Using the templateString property and dojo/text, we specify our template's contents.
  • 使用templateString属性和dojo/text我们指定了模板的内容。
  • We set our baseClass. This will be applied to our root node, which in our case is the div in our template.
  • 我们设置了baseClass 样式。这个值将会添加到我们的根节点上,在本例中即模板中的div。
  • We set up a reference for our animation, to be worked with in a moment, as well as a couple of colors for the animation.
  • 我们设置了一个动画的变量,等会我们就会用到,以及动画需要用到的两种颜色值。

This is all well and good, and if we stopped here, it would actually work as a very basic widget that displayed information. However, we can add in a couple of methods to make things a bit safer. We're going to add:

这很好,如果我们止步于此,它会作为一个基本的小部件显示信息。不过,我们可以添加几个方法使之更安全。我们将要添加:

  • some logic in postCreate (the most commonly extended lifecycle method from _WidgetBase)
  • 在postCreate方法(继承自_WidgetBase的生命周期中最常见的方法)中添加部分逻辑
  • a custom property setter for the avatar property
  • 为avatar(头像)添加一个自定义的属性设置器
  • a utility function to make it easy to change background color.
  • 一个更改背景色的工具函数

Let's visit each one of those.

我们一一道来:

The postCreate method is where we want to put the bulk of our work. It's called once our widget's DOM structure is ready, but before it's been inserted into the page. It's typically the best place to put any sort of initialization code.

postCreate中我们将会添加我们的大部分代码。微件的Dom结构初始化完成后会调用一次这个方法,此时微件还未添加到页面上。这通常是放置我们的初始化代码的最好的地方。

postCreate: function(){
    // Get a DOM node reference for the root of our widget获取微件根Dom节点
    var domNode = this.domNode;

    // Run any parent postCreate processes - can be done at any point调用父类postCreate方法,可以在任意位置调用
    this.inherited(arguments);

    // Set our DOM node's background color to white -设置根节点背景颜色为白色
    // smoothes out the mouseenter/leave event animations保证移入移出事件动画平滑
    domStyle.set(domNode, "backgroundColor", this.baseBackgroundColor);
    // Set up our mouseenter/leave events 设置鼠标移入移出事件
    // Using dijit/Destroyable's "own" method ensures that event handlers are unregistered when the widget is destroyed使用own方法确保回调函数在微件销毁时解除绑定
    // Using dojo/mouse normalizes the non-standard mouseenter/leave events across browsers使用dojo/mouse实现跨浏览器的鼠标移入移出事件
    // Passing a third parameter to lang.hitch allows us to specify not only the context,将lang.hitch作为第三个参数使得我们不仅能够设置上下文
    // but also the first parameter passed to _changeBackground还可以设置传给_changeBackground方法的第一个参数
    this.own(
        on(domNode, mouse.enter, lang.hitch(this, "_changeBackground", this.mouseBackgroundColor)),
        on(domNode, mouse.leave, lang.hitch(this, "_changeBackground", this.baseBackgroundColor))
    );
}

Here, we're setting some style based on our baseBackgroundColor property, and then setting up some onmouseenter/onmouseleave events, so that as people mouse over the DOM node, our custom _changeBackground function is called. Let's take a look at that:

现在,我们要通过baseBackgroundColor属性设置一些样式,之后再设置鼠标移入移出事件,这样当用户的鼠标移入微件的Dom节点,自定义的_changeBackground方法就会执行。我们来看一下:

_changeBackground: function(newColor) {
    // If we have an animation, stop it如果已经在动画中,先停止该动画
    if (this.mouseAnim) {
        this.mouseAnim.stop();
    }

    // Set up the new animation配置新动画
    this.mouseAnim = baseFx.animateProperty({
        node: this.domNode,
        properties: {
            backgroundColor: newColor
        },
        onEnd: lang.hitch(this, function() {
            // Clean up our mouseAnim property 清理mouseAnim属性
            this.mouseAnim = null;
        })
    }).play();
}

Why is this method called _changeBackground and not just changeBackground? It is named with a leading underscore to indicate that users of this widget should treat the method as though it were private, and not something to be used directly. It's a common Dojo pattern to prefix methods or objects with an underscore in order to indicate that they shouldn't be used directly. It doesn't actively stop users from using those methods, but is a useful implicit indication of sorts that "Hey, this isn't meant for general use".

为什么这个方法名为_changeBackground而非changeBackground?这个方法名以_开头标明微件的用户需要将其视为一个private方法,不能直接调用。这是dojo中一种通用的方式,在方法或变量名称之前添加_来表明这是一个私有变量不能直接调用。虽说实际上用户还是能够调用它,但是这是一个有用的隐式表示“嘿,这不是一般使用的”。

We're checking our mouseAnim property to see if there's an animation there, and if we have something there, we're calling stop to stop it, as a means of being safe. Then, we simply set up the new animation and save it back into mouseAnim, then start it playing. This example is very similar to an effect as demonstrated in the Animation tutorial, though with some different colors.

我们检测mouseAnim属性查看是否已经有一个动画,如果是,则执行动画的stop方法停止该动画,以保证安全。之后,配置新的动画并赋给mouseAnim变量,并执行动画。这与Animation教程中展示的例子很像,只是颜色不同。

Finally, remember how we had concerns earlier that a user might not have an avatar, and we set up a default one? We can set up a custom setter function for attributes, which will automatically be called any time that value is set, either when the widget is being created or if someone calls myWidget.set("avatar", somePath). The name of the method is special, and maps to the name of the property—in this case, for avatar, our name will be _setAvatarAttr.

最后,记不记得我们关注过一个用户可能没有头像,我们给头像赋了一个默认值的事?我们可以为属性添加一个自定义的设置器方法,在微件创建或使用myWidget.set("avatar",somPath)使得avatar属性改变时,这个方法会自动调用。方法的名字是特别的,与属性名对应-在本例中,作为avatar的设置器,方法名应该是_setAvatarAttr.

_setAvatarAttr: function(imagePath) {
    // We only want to set it if it's a non-empty string当imagePath不为空时才会对avatar赋值
    if (imagePath != "") {
        // Save it on our widget instance - note that保存到微件实例中-注意
        // we're using _set, to support anyone using我们使用_set,支持所有使用该微件的用户
        // our widget's Watch functionality, to watch values change监听方法执行,监测变量改变
        this._set("avatar", imagePath);

        // Using our avatarNode attach point, set its src value通过attach Point方式设置image节点的src
        this.avatarNode.src = imagePath;
    }
}

Starting with Dojo 1.6, all dijit/_WidgetBase widgets include dojo/Stateful in their inheritance chain, which means that users can actively watch for value changes. We use _set within our setter to ensure that all watch calls are properly fired, and then we use our avatarNode attach point to set the image's src attribute to the value being set. By wrapping it in a check for the string not being empty, we're trying to avoid cases where the avatar property may be there, but with nothing but an empty string. This way, we get our default image if we have no value, with a bit of a safety check.

使用Dojo1.6的话,所有的基于dijit/_WidgetBase的微件都包含dojo/Stateful,意味着用户可以监测变量值改变。我们在设置器中使用_set方法保证所有的watch都可以执行,之后我们使用avatarNode attchPoint 来设置image图片的src属性。在设置之前我们会监测imagePath是否是空值,避免为图片设置空值。这样,当avatar没有值时默认值就起作用了,增加了一个安全验证。

To use the widget, we do the following:

为了使用微件,我们需要按一下方式操作:

<div id="authorContainer"></div>
require(["dojo/request", "dojo/dom", "dojo/_base/array", "myApp/widget/AuthorWidget", "dojo/domReady!"],
    function(request, dom, arrayUtil, AuthorWidget){
    // Load up our authors 加载作者数据
    request("myApp/data/authors.json", {
        handleAs: "json"
    }).then(function(authors){
        // Get a reference to our container 添加一个指向container的变量
        var authorContainer = dom.byId("authorContainer");

        arrayUtil.forEach(authors, function(author){
            // Create our widget and place it 创建微件并添加到页面上
            var widget = new AuthorWidget(author).placeAt(authorContainer);
        });
    });
});

With all of these items in place, we have a working widget! However, as you can see, it's not exactly pretty yet.

这些都做完之后,我们就有了一个可以正常运行的微件!不过,如你所见,略丑。

View Demo

Step 5: Style as appropriate
第五步:使用样式美化

One of the benefits of using dijit/_WidgetBase, as mentioned above, is that it gives us a baseClass that we can work off of for styling purposes. Using that, we can create some fairly simple styling. We have a folder under our AuthorWidget space just for css, so let's create an AuthorWidget.css file in there.

上面已经提到过的,使用dijit/_WidgetBase的一个好处就是,我们可以使用baseClass属性来设置样式。使用该属性,我们可以创建一些简单的样式。在AuthorWidget中我们有一个专门的放css文件的目录,我们可以在此创建一个AuthorWidget.css文件。

/* myApp/widget/css/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;
}

Since we know our baseClass is authorWidget, we can work off of that. Also, if you recall, in the template, we set a class on the avatar of ${baseClass}Avatar, so we can use authorWidgetAvatar in our styling.

已知我们的baseClass值为authorWidget,我们可以对其进行配置。同时,如果在模板中再次调用,我们设置avatar的class为${baseClass}Avatar,因此将使用authorWidgetAvatar。

Now, with that in place, we just need to add the CSS to our head on our page, and we have a nicer looking author list!

现在,这些都完成之后,我们只需要将css文件添加到页面的head标签中,我们就有了一个好看点的作者列表了!

View Demo

Summary

总结:

As you can see, using dijit/_WidgetBase and dijit/_TemplatedMixin makes it fairly easy to create a custom widget. We were able to quickly set up a template, and with a little bit of work, we were able to create our AuthorWidget class based off of dijit/_WidgetBase and dijit/_TemplatedMixin.

It's worth noting that most widgets in Dijit itself are built using these same tools, and we've only scratched the surface of what they can do. Be sure to follow through some of the links below for more information!

如你所见,基于dijit/_WidgetBase 和 dijit/_TemplatedMixin 创建自定义微件那是相当的简单。我们快速的配置了模板,再加上很少量的工作,我们就创建了基于dijit/_WidgetBase 和 dijit/_TemplatedMixin的AuthorWidget类。

值得注意的是Dijit中大多数的微件也都是基于这些工具创建的,我们只是了解了一点皮毛。一定要通过下面的连接继续学习。

Resources

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值