Dojo 1.6 官方教程: 创建自定义Dojo小部件(Widget)

article/2025/9/13 5:30:01
在这个教程中,我们将会演示如何利用Dojo 和Dijit框架来创建自定义的小部件。 主要会使用到dijit._Widget 和dijit._Templated 基类和mixin。
对dijit框架的基础知识,可以参看前两篇教程

难度:中等
适用Dojo版本: 1.6

作者:Brian Arnold
Brian Arnold is a software engineer at SitePen, Inc. He has a lovely wife, two cute dogs, is an active member of (and presenter at) Webuquerque, and ranks among the top 3% of fake guitarists in Rock Band.
原文连接:http://dojotoolkit.org/documentation/tutorials/1.6/recipes/custom_widget/

译者 : feijia  tiimfei@gmail.com

Dojo的Dijit 库包含了丰富的界面小部件(Widgets),通过使用这些小部件,可以打造出强大的Web应用界面,从高级的表单元素,到复杂页面布局。

但是对于一些较复杂的应用,开发者仍会碰到做进一步定制的需求,例如非常复杂的信息展示需求。你当然可以手动的自己构造DOM来展示数据,但是如果利用Dijit已经提供的框架和工具,我们可以快速的开发出灵活又强大的自定义小部件。


场景


假设我们需要开发一个能展示所有Dojo教程作者的简介信息的页面。我们手头的数据源是如下的JSON数据:
[
{
"name": "Brian Arnold",
"avatar": "/includes/authors/brian_arnold/avatar.jpg",
"bio": "Brian Arnold is a software engineer at SitePen, Inc., ..."
},
/* More authors here... */
]

我们的需求是把这些信息以下面的DOM结构展示在页面上:
<body>
<!-- Headers and whatnot -->
<h2>Authors</h2>
<div id="authorContainer">
<!-- Authors go here! -->
</div>
</body>

当然,我们希望这个展示页面可以再加点效果,例如当鼠标移到某个作者上时,背景色可以淡入显示。

最终展示的页面效果如下:



解决方案



我们可以创建一个自定义的Dojo小部件来实现这一需求,分如下几个步骤:

1. 创建必要的小部件目录结构
2. 创建展示单个作者的HTML标记
3. 在#2的基础上把这些标记变成Dijit模板
4. 使用Dojo.declare 来创建我们的小部件类
5. 进行必要的CSS美化


第一步是为自定义小部件创建必要的目录结构


虽然这一步看起来有些多余,但是为你开发的自定义小部件安排一个合理的目录结构是一种良好的编程习惯的。在这里我会创建一个名为custom的目录,作为存放我所有代码和模板的命名空间。当然目录的名称完全取决于你自己,可以是你就职的机构的名称。 然后我为即将创建的自定义小部件取名叫AuthorWidget,所以我会创建一个AuthorWidget目录,这个目录会包含与这个小部件相关的css和html模板以及源代码。 最终的文件结构会如下图所示:


第二步  创建展示单个作者的HTML片段


作为第一个自定义小部件,我们会试着从最简单的做起。它仅仅在页面上展示一些数据。

这里我们使用如下的简单片段来展示一个作者的信息。最外层是一个容器div,记住创建Dijit模板时一定要有一个唯一的根节点。这里这个容器div就是我们模板的根节点。然后我们使用一个H3标签来展示作者名字,一个img标签来展示头像,一个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>

第三步 把HTML片段变成Dijit模板


当使用Dijit._Templated (我们小部件的基类)时,你对模板会有许多操作的方式:

1. 你可以在模板中自动插入变量值
2. 你自己在模板中定义附着点元素(attach point),这样你在widget中引用并编程操纵这些DOM元素
3. 可以在模板中的元素上设定DOM事件的处理函数

在我们这个例子里,我们主要会用到自动的变量值插入。现在我们来创建文件: custom/AuthorWidget/templates/AuthorWidget.html . 文件的内容基本和第二步中的HTML片段相似,只是会添加一些Dijit模板特有的属性。

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


在这一模板中:

1. 我们使用 ${attribute}的语法来直接插入一些值,例如这里我们插入了作者姓名${name}

2. 类似的,还有一种语法是${!attribute}. 这两者的区别是,${!attribute} 会对值原样插入而不做转义。在这里我们在${!bio}位置要替换的值会包含一些HTML标记,我们不希望Dijit._templated 对这些标记做转义。

3. 所有基于dijit._Widget的小部件都有一个baseClass属性。

4. 在上述模板中,我为每个节点都定义了附着点属性,这样在我的代码中就可以直接使用诸如myAuthor.nameNode 这样的代码直接访问到H3节点。

你也许已经注意到了我在模板中的img标签里没有指定src属性值。 那么如果我们的数据里某个作者没有包含头像图片怎么办?我们需要能够在创建我们的小部件时自动设定处理这类情形的默认值。 后面的步骤会进一步解释如何解决这个问题。


第四步 使用dojo.declare创建小部件的类

这一步我们要在custom目录里创建AuthorWidget.js 。 并且我们添加一个默认的头像的图片文件。

创建之后的目录和文件结构如下:


在AuthorWidget.js 里我们将使用dojo.declare 创建我们的小部件类。 如下:

// custom.AuthorWidget
dojo.provide("custom.AuthorWidget");
// 声明依赖的模块和基类
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
// Create our widget!
dojo.declare("custom.AuthorWidget", [dijit._Widget, dijit._Templated], {
/* 我们的自定义小部件属性将会被添加在这里 */
}) // and that's it!


这段代码中:

1. 我们使用dojo.provide 来声明我们这个js文件提供了一个名为custom.AuthorWidget 的资源。 从1.6开始, Dojo加入了对异步模块加载机制的支持(AMD),但是为了简单起见,这里我们沿用了传统的Dojo模块声明和加载系统(dojo.provide 和dojo.require)。

2. 我们使用了dojo.require 来引用我们的小部件所依赖的模块 (dijit._Widget 和dijit._Templated 这两个基类)

3.我们使用dojo.declare 声明了一个类, custom.AuthorWidget,该类使用dijit._Widget 和dijit._Templated作为基类。


注意在这个js文件中,我们不需要使用dojo.ready ---- 这是因为我们在开发一个dojo的模块。 而Dojo的模块加载系统会保证你所依赖的模块都加载成功后再运行加载后续的模块。

当然,上面的代码还只是一个空架子,要让它真的可以工作,我们还需要给我们的小部件设置一系列属性。 下面是加上了属性设置的dojo.declare的代码片段。


dojo.declare("custom.AuthorWidget", [dijit._Widget, dijit._Templated], {
// 设置一些默认值
// These typically map to whatever you're handing into the constructor
name: "No Name",
// Using dojo.moduleUrl, we can get a path to our AuthorWidget's space
// and we want to have a default avatar, just in case
avatar: dojo.moduleUrl("custom.AuthorWidget", "images/defaultAvatar.png"),
bio: "",
// 加载我们的模板 - important!
templateString:
dojo.cache("custom.AuthorWidget", "templates/AuthorWidget.html"),
// 将会被应用到模板根节点的css类名
baseClass: "authorWidget",
// 指向我们背景动画对象的引用
mouseAnim: null,
// 用于背景的颜色属性
baseBackgroundColor: "#fff",
mouseBackgroundColor: "#def"
});


首先我们定义了一些属性用于保存作者的基本信息:姓名,简介和头像。我们也提供了这些属性的默认值。 在设置头像的默认值时,我们使用了dojo.moduleURL来引用当前小部件所在的路径,并访问该路径下的图片文件夹。

通过使用templateString属性,和dojo.cache 我们加载了之前定义的模板文件

设置baseClass属性,该属性会被设置为小部件的DOM根节点的CSS类。 在这里就是我们模板中的最外层div节点。

我们还留出了用于设置动画的属性和背景色的属性。

到目前为止,我们的小部件已经初具雏形,实际上它已经可以运行并显示一些简单的信息了。 当然我们还需要进一步完善它。

接下来我们会添加postCreate方法,一个定制的头像设定方法,已经一个辅助方法用来变换背景颜色。

postCreate方法是用来添加我们的工作逻辑的主要入口。它的调用时机是在小部件的DOM结构成功创建后,但是还没有被添加到页面的DOM树之前(也即用户看不到这个小部件的DOM节点)。因此通常我们把初始化的工作放在这个方法中。

postCreate: function(){
// Get a DOM node reference for the root of our widget
var domNode = this.domNode;
// Run any parent postCreate processes - can be done at any point
this.inherited(arguments);
// Set our DOM node's background color to white -
// smoothes out the mouseenter/leave event animations
dojo.style(domNode, "backgroundColor", this.baseBackgroundColor);
// Set up our mouseenter/leave events - using dijit._Widget's connect
// means that our callback will execute with `this` set to our widget
this.connect(domNode, "onmouseenter", function(e) {
this._changeBackground(this.mouseBackgroundColor);
});
this.connect(domNode, "onmouseleave", function(e) {
this._changeBackground(this.baseBackgroundColor);
});
}



在postCreate里我们利用baseBackgroundColor属性设置了domNode的背景色,并且设置了onmouseenter和onmouseleave的事件处理函数,因此当鼠标悬停在我们的DOM节点上时,_changeBackground 函数就会被调用。 下面我们来看看这个函数:

_changeBackground: function(toCol) {
// If we have an animation, stop it
if (this.mouseAnim) { this.mouseAnim.stop(); }
// Set up the new animation
this.mouseAnim = dojo.animateProperty({
node: this.domNode,
properties: {
backgroundColor: toCol
},
onEnd: dojo.hitch(this, function() {
// Clean up our mouseAnim property
this.mouseAnim = null;
})
}).play();
}


注: 为什么我们要在这个方法名前加下划线呢?这是一种dojo的命名规范,在方法和对象属性前加下划线表示该方法或对象是类内部成员,不应被用户直接使用。虽然JavaScript语言没有强制禁止这类调用,但是这是一个良好的编程习惯,也是一种提示用户正确使用API的方法。

在这个方法中,我们先检查是否当前有动画正在执行,如果有的话我们先停止它。 然后在设置新的动画并保存在mouseAnim中,并开始播放。 这个例子十分类似于我们在Dojo动画教程http://dojotoolkit.org/documentation/tutorials/1.6/animation/ 中的效果,只不过颜色有所差别。


最后,我们需要解决一个问题,就是如果某个作者没有提供头像图片,我们需要给他设置一个默认的头像。 这里我们通过创建一个定制的属性设置方法(custom setter) 来实现。 这类方法有固定的命名规则, _setXXXAttr  其中XXX是你要设置的属性的名称(首字母需大写)。 例如我们这里要设置的是“avatar属性,因此我们需要的方法名是_setAvatarAttr

在小部件被创建或者用户使用属性设置方法:myWidget.set("avatar",somePath) 时,定制属性方法就会被调用.
_setAvatarAttr: function(av) {
// We only want to set it if it's a non-empty string
if (av != "") {
// Save it on our widget instance - note that
// we're using _set, to support anyone using
// our widget's Watch functionality, to watch values change
this._set("avatar", av);
// Using our avatarNode attach point, set its src value
this.avatarNode.src = av;
}
}



这个方法主要是做一个安全检查,在用户传入空字符串时,就使用默认的头像。

从Dojo1.6开始,所有基于dijit._Widget的小部件都会自动继承dojo.Stateful ,这个类加入了对类属性的变化的监控。我们在_setAvatarAttr 中使用this._set() 方法就是为了遵循dojo.Stateful的规则,保证所有属性的变化可以被检测到。


所有这些都完成后,我们现在有了一个可以工作的小部件。 虽然看起来还不是那么美观。



查看示例

第五步: 美化


使用Dijit._Widget的好处之一,就是他提供了一个baseClass属性作为根节点的CSS样式类。之前创建的目录结构中有专门的css目录,接下来我们在css目录中创建一个AuthorWidget.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;
}


可以看到我们定义了authorWidget 类,这个类是baseClass所制定的,它会被应用到模板的div根节点上。 同时,模板中还有 ${baseClass}Avatar类,因此我们定义了authorWidgetAvatar类。(这只是一些最基本的美化,别对我要求太高了,我不是一个设计师)

最后一步在页面的header部分加入我们的css文件,我们就有了一个美观的作者列表小部件啦!



查看示例

小结


这个教程里,我们看到使用dijit._Widget 和 dijit._Templated 作为基类,创建一个自定义的小部件很简单。 我们可以快速的创建模板,添加自定义逻辑,并且定义自己的css来美化小部件的外观。

虽然这个例子很简单,但是你要知道绝大多数Dijit里的小部件都是基于这两个基类开发的,使用这些工具可以开发出强大的界面组件。下面的参考阅读中列出了一些很好的文档可供你参考。



    * dijit._Widget on the Dojo Reference Guide
    * dijit._Templated on the Dojo Reference Guide
    * Tutorial on dojo.declare
    * Dojo Reference Guide: Writing Your Own Widget



http://chatgpt.dhexx.cn/article/9EfvlW5D.shtml

相关文章

(3)Dojo学习之Class

引言dojo中的类 1创建一个简单的类2类和模块结合使用3关于类的继承 需求 1需求一2需求二很重要21代码实现22程序出错23程序运行3 需求三 再次介绍require加载模块总结 1本博客代码的下载地址dojo_class 1.引言 说到面向对象&#xff0c;我们就不得不提一下类&#xff0c;在原生…

探索dojo/json

难度&#xff1a;中等 Dojo版本&#xff1a;1.7 原作者&#xff1a;Kris Zyp 译者&#xff1a;Oliver (zhuxw1984gmail.com) 原文链接&#xff1a;http://www.sitepen.com/blog/2012/09/21/exploring-dojojson/ &#xff08;年初翻译过Kris Zyp的另一篇介绍dojo/json的文章&…

dojo是什么?

现在Web端vue、React、Angular大行其道&#xff0c;安卓端js也有React Native等&#xff0c;那么学习dojo有什么意义呢&#xff1f; 有些老的项目还是用的dojo。。。。例如arcgis javascript api等等 https://dojotoolkit.org/documentation/tutorials/1.10/hello_dojo/ 为什…

Dojo入门篇

Dojo是一个JavaScript实现的开源DHTML工具包&#xff0c;Dojo最初的目标是解决开发HTML应用程序中遇到的一些长期存在的问题&#xff0c;然而现在Dojo已经成为了开发RIA应用程序的利器。 Dojo让Web页面具有动态能力&#xff0c;我们可以在其他支持JavaScript的环境中使用Dojo。…

dojo中的类

使用arcgis api for js 4.*进行地图的web前端开发&#xff0c;就不得不与dojo打交道。dojo是一个框架&#xff0c;自成体系&#xff0c;比如它对类的支持&#xff0c;有自己的一套。众所周知&#xff0c;js不是面向对象语言&#xff0c;没有类这一说&#xff0c;都是用函数来模…

dojo学习教程

Dojo 作为最著名的 Ajax 开源项目之一&#xff0c;不仅让 Web 程序员可以免费获得和使用其框架进行 Web 应用的开发&#xff0c;更吸引了大量的开发者对其不断的扩充&#xff0c;开发新的组件。DojoX 就是在这样的开发社区中产生的。DojoX 是一组基于 dojo 的开源项目的集合&am…

Dojo与jQuery综合比较分析

最近Dojo和jQuery双双发布了最新的1.8版本,有着相同版本号的两个Javascript库也有许多核心的相同之处:相同的资源加载机制AMD、相同的选择器 引擎Sizzle等。作为业界知名的Javascript库,Dojo和jQuery在各自领域有着为数众多的拥护者。不过正所谓一把钥匙开一把锁,对一个项目…

Dojo简述

Dojo是一个JavaScript实现的开源DHTML工具包。Dojo的最初目标是解决开发DHTML应用程序时遇到的一些长期存在的历史问题。 【注&#xff1a;DHTML是一种使HTML页面具有动态特性的艺术。DHTML是一种创建动态和交互WEB站点的技术集合。对大多数人来说&#xff0c;DHTML意味着HTML…

ibatis简介

背景 介绍ibatis实现之前&#xff0c;先来看一段jdbc代码: Class.forName("com.mysql.jdbc.Driver"); String url "jdbc:mysql://localhost:3306/learnworld"; Connection con DriverManager.getConnection(url, "root","learnwo…

Ibatis使用

部分参考&#xff1a;https://blog.csdn.net/shixiaoguo90/article/details/49949445 Apache iBatis是当前IT项目中使用很广泛的一个半自动ORM框架&#xff0c;区别于Hibernate之类的全自动框架&#xff0c;iBatis对数据库的操作拥有更加灵活的控制&#xff0c;对于那些经常需…

iBatis-iBatis配置环境(eclipse+mysql)

在使用JAVA框架前要配置其环境&#xff0c;比如使用Hibernate、Struts等都需要对其环境进行配置&#xff0c;当然使用iBatis开发之前&#xff0c;也需要配置iBatis环境。 一、iBatis环境的配置只需三步 1.引入jar包 2.配置sqlMapConfig.xml文件 3.配置SqlMap.xml文件 二、概览i…

什么是IBatis

什么是IBatis 我一直想写一篇关于持久化数据访问的文章&#xff0c;可是我不知道怎么开始介绍&#xff0c;前两天晚上睡觉的时候突然有了一些开始写的想法&#xff0c;所以我决定今天动手开始写一点东西。头一次写这样的文章&#xff0c;希望大家不要见怪。 现在网上很多人学习…

ibatis-Spring 整合

这两天一直在研究ibatis与spring的整合 一个小小的demo搞的我头晕目眩的&#xff0c;但程序一旦跑起来了&#xff0c;突然有一种豁然开朗&#xff0c;重见天日&#xff0c;感觉生活很美好的感觉&#xff01;&#xff0c;也许&#xff0c;这就是那一行行的代码带给我们的不同享受…

ibatis与spring整合

这两天一直在研究ibatis与spring的整合 一个小小的demo搞的我头晕目眩的&#xff0c;但程序一旦跑起来了&#xff0c;突然有一种豁然开朗&#xff0c;重见天日&#xff0c;感觉生活很美好的感觉&#xff01;&#xff0c;也许&#xff0c;这就是那一行行的代码带给我们的不同享受…

iBatis--iBatis 是什么?

一.为啥使用iBatis&#xff1f; 在 Hibernate、JPA 这样的一站式对象 / 关系映射&#xff08;O/R Mapping&#xff09;解决方案盛行之前&#xff0c;iBaits 基本是持久层框架的不二选择。即使在持久层框架层出不穷的今天&#xff0c;iBatis 凭借着易学易用、轻巧灵活等特点&am…

iBatis-iBatis基本操作(增删改查)

在上一篇iBatis博客中已介绍了如何配置iBatis环境&#xff0c;这篇博客主要进行介绍一些iBatis的基本操作实现。iBatis的增删改操作都需要操作SqlMap,DAO层Manger,POJO 这几个类来实现。下面分别介绍一下iBatis基本操作的实现: 一.iBatis的添加数据方式 这里介绍两种iBatis添加…

Ibatis与Mybatis的区别—侧重于Ibatis

目录 一、什么是Ibatis&#xff1f; 1、iBatis是一款轻量级的持久化框架 2、iBatis最大的特点是将SQL语句与Java代码分离 3、iBatis具有以下几个关键组成部分&#xff1a; 二、Ibatis与Mybatis的区别 1、基本信息不同 2、开发时间不同 3、配置方法不同 三、Ibatis与My…

ibatis使用方法

转载。怕原地址丢失&#xff0c;备份。。 http://lyb520320.iteye.com/blog/586628 http://lyb520320.iteye.com/blog/586800 iBATIS3.0学习&#xff08;一&#xff09;使用iBATIS3.0完成增删改查 博客分类&#xff1a; iBATIS3 iBATIS Apache Spring SQL JDBC 使用iBATIS3.0完…

IBatis使用浅析

ibatis 历史 Eight years ago in 2002, I created the iBATIS Data Mapper and introduced SQL Mapping as an approach to persistence layer development. Shortly thereafter, I donated the iBATIS name and code to the Apache Software Foundation. The ASF has been th…

IBatis的使用

IBatis的使用 1、IBatis是什么 回顾之前前端访问后端的整个流程&#xff1a; View ------ >Controller --------> Service ---------> DAO ------> 数据库 View :前端jsp/HTML Controller&#xff1a;Servlet/SpringMVC Service &#xff1a;Spring DAO&…