Creating new Dojo Widget

This tutorial demonstrates how to create a Dojo widget that displays a list of items. The widget implements paging and fetches data from the server using ajax request.

Widgets are everywhere on the screen of your PC (when it is on, of course). Buttons, labels, panels, edit boxes are interface components and “interface component” is just another name of the thing that is called “widget”. Widgets can be simple like labels or complex like spreadsheets. Some of them consist of thousands of another widgets, some of them are atomic and implemented only with underlying API. On the web, a thing which I’m constantly talking about, the “underlying API”, or methods that the developer can use for widget building, includes HTML, JavaScript, DHTML, CCS, Ajax and many other technologies.

A lot of widgets have been already created and you can use almost all of them in your Web applications. The market leaders offer you robust toolkits with widget sets that contain general-purpose interface components like buttons, textboxes, trees. Some of them also include components for supporting various animation effects. A few teams focus mainly on one widget: rich editors or accessible menu. These teams create excellent components, but an advantage of the toolkit widget sets is an integration. Toolkit widgets can work together on one form: controls, validators, I/O classes can be easily placed and configured on a Web page.

So, if there are a lot of existent components, why to create new widgets? The answer rises when you want to reuse a group of tightly connected interface components of your application. It can be very simple and trivial solution to use “copy/paste” reusing when you want to reuse the code only once, but each such duplication increases maintenance time. Each defect in the duplicated code should be fixed twice. When you remember where a second copy of the code is. Or, which I wish never happen, a third one.

In this article I will show you how to create a new widget with the dojo toolkit. I will use the parts of my previous articles Model-View-Controller (MVC) with JavaScript and Create Ajax Login page so consider reading them if you have not yet. I plan to create a widget that displays a list of items, received from the server. I will call it "the DataList widget". It will support columns and data paging.

Dojo widget building requires a brief foreword here. Regular Dojo widget consists of three parts: JavaScript widget class that defines how parts of the widget interact with each other, HTML template with widget layout and CSS file with used styles. When the widget is created, either automatically or manually, Dojo widget engine loads template, javascript and css files, parses the template and creates class instance. After a few initialization functions, widget can be inserted into the page object model. The user interacts with widget parts with mouse or keyboard and corresponding event functions handle this interaction.

So, the widget creating plan can be as follows:

  1. Clarify the idea of a widget. Specify, how the widget should look like and which user input events should be handled. Write down a few use cases. Define configuration parameters of the widget.
  2. Develop a presentation part. Create HTML and CSS files with the template of the widget.
  3. Add behavior. Create JavaScript widget class with initialization and event handlers.
  4. Put all together. Create sample page that implements use cases. This part may require server-side programming.

Following the plan, I need to gather and polish widget requirements. So, what I need is the grid-like widget, which displays columnar data. Presentation of the column can be configured by corresponding CSS rule. Grid should support paging: it should display prev/next links below the widget. And of course, I want it obtaining data using ajax request. Ajax requests will be served by PHP script, created with Zend Framework.

The functionality defines a design of the widget. It is not very different from any other grid and can look like the following HTML snippet:

LIST OF ARTICLES
Creating new Dojo Widget
This tutorial demonstrates how to create a Dojo widget that displays a list of items. The widget implements paging and fetches data from the server using ajax request.
Issued at 2006-Aug-23 by Alexander
Model-View-Controller (MVC) with JavaScript
The article describes an implementation of Model-View-Controller software design pattern in JavaScript. Created classes conform to Dojo toolkit class building concepts: dojo.lang.declare creates classes and dojo.event.connect supports low coupling of MVC.
Issued at 2006-Aug-04 by Alexander

As you can see, the grid contains title, section with items, and a pager with navigation links. Each item contains four data fields: title, brief description, information section and operation links. The first three columns are very similar and just display text data. The last column, with operation links, is very complex and it would be better to consider it as a customization of the instantiated widget. So, the widget should create title, pager, and items with fields and provide a handler, in which developer can modify the content of the row.

The above HTML snippet is created with “div” tags and CSS. You can review its anatomy using “View source” option of your browser and I continue with creating HTML template and stylesheet file of the widget.

DataList.html

<div class="DataList">
 <div class="Title">
  <div class="Content">
   <div class="Text">${this.title}</div>
  </div>
 </div>
 <div class="Items">
  <!-- This code is commented because 
        items will be created at runtime.
  <div class="Item">
   <div class="Content">
    ...
   </div>
  </div>
  -->
 </div>
 <div class="Pager">
  <div class="Container">
   <div class="Content">
    <div class="Prev"><a href="#">${this.prevPageText}</a></div>
    <div class="Next"><a href="#">${this.nextPageText}</a></div>
    <div class="Pages">
     <!-- Pages section will be populated right after the list data
          is fetched from the server. -->
    </div>
   </div>
  </div>
 </div>
</div>

DataList.css

.DataList {
	border: 2px solid #aaaaaa;
	background: #ffffee;
	text-align: left;
}

.DataList .Title {
	margin: 0.7em;
	border: 1px solid #909090;
	background: white;
}

.DataList .Title .Content {
	text-align: center;
}

.DataList .Title .Content .Text {
	margin: .3em;
	letter-spacing: .1em;
	color: #303030;
}

.DataList .Items {
}

.DataList .Items .Item {
	margin: 0.7em;
	border: 1px solid #909090;
	background: white;
	text-align: left;
}

.DataList .Items .Item .Content {
	margin: .2em;
}

.DataList .Pager {
	margin: 0.7em;
	border: 1px solid #909090;
	background: white;
	text-align: left;
}

.DataList .Pager .Container {
	font-size: .8em;
	color: #303030;
	margin: .2em;
	height: 1.5em
}

.DataList .Pager .Container .Content {
	margin: .3em;
}

.DataList .Pager .Container .Content .Prev {
	float: left;
}

.DataList .Pager .Container .Content .Next {
	float: right;
}

.DataList .Pager .Container .Content .Pages {
	text-align: center;
}

As you can see, the CSS defines rendering rules for template elements. The “${...}” variables in the template will be substituted with the corresponded widget object properties. The template has empty “Items” section, because it will be populated at runtime. So, with these two obligatory parts of the widget (template and style) I can create a skeleton of this future user interface component by extending dojo.widget.HtmlWidget class. In the following file pay attention to “templatePath” and “templateCssPath” properties—their values indicate that the files, which define the widget, are located in one folder.

DataList.js

dojo.widget.defineWidget(
	"user.DataList",
	dojo.widget.HtmlWidget,
	{
		// current page
		page : 1,

		// total number of pages
		pages : 1,

		// DataList title
		title : "DataList",

		// Items array
		items : [],

		// title of the next page link
		nextPageText : "next page",

		// title of the prev page link
		prevPageText : "previous page",

		// url to which an ajax request will be sent
		// for obtaining data from the server
		dataUrl : "",

		// definitions
		templatePath : new dojo.uri.Uri("", "./DataList.html"),
		templateCssPath : new dojo.uri.Uri("", "./DataList.css")
	}
);

Immediately after that you can place the DataList widget on any of your Web pages using the following method:

<!-- This section should be added to the page only once -->
<script type="text/javascript" src="~/path/to/dojo.js"></script>
<script type="text/javascript">
	dojo.require("dojo.lang.*");
	dojo.require("dojo.io.*");
	dojo.require("dojo.widget.*");
	dojo.require("dojo.html.*");
</script>
<script type="text/javascript" src="DataList.js"></script>
<!-- The following div with dojoType attribute specifies
	where an instance of the DataList widget will be rendered -->
<div dojoType="DataList" title="DataList Example"></div>

Notice the “title” attribute of the “<div dojoType="DataList" title="DataList Example">” tag. It shows how the parameters are passed from the tag, which defines widget location and configures it, to the widget object. “title” attribute sets “title” widget property and if you want to set another property, just add the corresponding attribute.

Next point of the plan is “Add behavior” and it is time to breathe life into the widget. This can be done with event handlers, which can be specified using “dojoAttachEvent” attribute just in the corresponding template tag. Event binding can be done in two steps: adding the attribute to a tag in the template and adding event handling function into the widget class. So, for the DataList widget I need to handle clicks on the two interface elements: “previous items” and “next items” links. The following two code snippets show parts of the template and widget class after adding events and rendering methods:

<div class="Prev">
	<a href="#" dojoAttachEvent="onClick:prevPage">${this.prevPageText}</a>
</div>
<div class="Next">
	<a href="#" dojoAttachEvent="onClick:nextPage">${this.nextPageText}</a>
</div>
dojo.widget.defineWidget(
	"user.DataList",
	dojo.widget.HtmlWidget,
	{
		...
		// postCreate is a predefined event 
		// handler that is executed when widget 
		// is created and initialized
		postCreate : function () {
			this.fetch();
		},
		prevPage : function () {
			if (this.page > 1) {
				this.page--;
				this.fetch();
			}
		},
		nextPage : function () {
			if (this.page < this.pages) {
				this.page++;
				this.fetch();
			}
		},
		fetch : function () {
			if ("" != this.dataUrl) {
				// Send the request to the server and
				// rebuild the list of items
				var _this = this;
				dojo.io.bind({
					method : "GET",
					url: this.dataUrl.replace(/\{page\}/, this.page),
					mimetype: "text/json",
					load: function(type, data, evt) {
						_this.pages = data.pages;
						_this.items = data.items;
						_this.render();
					}
				});
			}
		},
		render : function () {
			// Recreate the list of items, calling this.onRenderItem(node)
			// each time when the item is rendered.
		},

		// Empty handler which is supposed to be called
		// by this.render() method when an item from the list is rendered.
		// node points to created "div" HTML element
		// with DataList data columns.
		onRenderItem : function (node) {
		}
	}
);

“fetch” method sends a request to the server, including current page number into the query string. “load” request handler, which is called when an answer is successfully received, changes internal widget variables and calls the “render” method. The “render” method contains massive code for rebuilding list of items—it is not presented here and you can review the attached source code if you want to analyze it in details.

An interesting thing, which requires an attention, is how the data is requested from the server and which response format is the best. As you may already guess, the request should include the current page, and server response should contain the total number of pages. What is less obvious, the request should contain the columns and supplementary row fields, which can be processed in the widget customization code. For example, when the row primary key should not be displayed as a column, but should be used by the “edit” or “delete” operation links. So, the whole response can be organized as the following JavaScript array:

{
	pages : 10,
	items : [
		// item #0
		{
			columns : [
				"Creating new Dojo Widget",
				"This tutorial demonstrates...",
				"Issued at ..."
			],
			fields : {
				id : 1,
				permalink : "http://www.alex..."
			}
		},
		// item #1
		...
	]
}

Last client-side issue on which I want to draw your attention is a method for customizing column style of the widget. You can change the style by defining CSS rule, which can change borders, margins, paddings and/or dozens of another presentation attributes, available in the CSS. The name of the rule consists of “.DataListColumn” plus column number. So, overwriting text color in the second column of the DataList can be achieved with the following CSS rule:

.DataListColumn2 {
	color: red;
}

The only one goal “Put all together” is left in the plan and it is the most interesting part of the widget developing. You have already seen how the widget is placed on the page, and here is a more complex example:

<div
	id="articles" 
	dojoType="DataList" 
	dataUrl="./data.php?page={page}" 
	title="LIST OF ARTICLES"
	prevPageText="newer articles"
	nextPageText="older articles"></div>

This example specifies more properties, then the previous one, and contains special “id” attribute. This attribute is used for selecting widget object programmatically in order to read or modify its properties. The following code snippet binds an unnamed function to the “onload” handler of the page:

// The handler selects widget and modifies its properties.
dojo.addOnLoad(function () {
	// select widget with name "articles"
	var articles = dojo.widget.byId("articles");
	// specifies onRenderItem handler, which adds new column to the
	// DataList columns
	articles.onRenderItem = function (node, item) {
		var columnDiv = node.appendChild(document.createElement("div"));
		dojo.html.setClass(columnDiv, "DataListColumn3");
		columnDiv.innerHTML = "<a href="" + item.fields.permalink 
			+ "">permalink</a>";
	};
});

The code above successfully adds the "articles" DataList widget. So I need to provide the widget with the data. I will use the Zend_Json class from Zend Framework for this, but, of course, you can populate it using any Json server-side library. The following server-side code returns one item to the DataList request. Again, you can check the attachment if you want to learn how to return more items.

data.php

<?php
set_include_path("/path/to/ZendFramework");
require_once "Zend/Loader.php";
spl_autoload_register(array('Zend_Loader', 'autoload'));
$data = array(
 "pages" => 1,
 "items" =>
  array(
   array(
    "columns" => array(
     "Creating new Dojo Widget",
     "This tutorial demonstrates ...",
     "Issued at 2006-Aug-23 by Alexander"
    ),
    "fields" => array(
     "id" => 1,
     "permalink" => "http://www.alex..."
    )
   )
  )
 );
echo Zend_Json::encode($data);
?>

Finally, you can try the online DataList example or download and learn the attachment with complete source code and examples.

Code in this article was tested with Dojo Toolkit 0.4.3, Zend Framework 1.0.0.

Comments

Want to integrate the same in rails

Hello Alex,

Great help from your widget creation aspects, well i am trying to create a widget very similar to your's in ROR using Datagrid provided by dojox.grid.

I would like to know what are the differences i need to add , can i create a simple datagrid widget class just taking in data and columns and making the widget work with some applied x,y co-ordinates on screen.

Please let me know if i can do anything in this line