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 other related 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 “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 javascript and template files (css should be loaded by user’s code), 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:

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 not so simple 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:

<div class="datalist">
  <div class="title">
    LIST OF ARTICLES
  </div>
  <div class="rows">
    <div class="row">
      <div class="row-cell row-cell-caption">
        Creating new Dojo Widget
      </div>
      <div class="row-cell row-cell-description">
        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.
      </div>
      <div class="row-cell row-cell-info">
        Issued at 2006-Aug-23 by Alexander
      </div>
      <div class="row-cell row-cell-links">
        <a href="#" onclick="return false;">permalink</a>,&amp;nbsp;
        <a href="#" onclick="return false;">edit</a>,&amp;nbsp;
        <a href="#" onclick="return false;">delete</a>
       </div>
    </div>
    <div class="row">
      <div class="row-cell row-cell-caption">
        Model-View-Controller (MVC) with JavaScript
      </div>
      <div class="row-cell row-cell-description">
        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.
      </div>
      <div class="row-cell row-cell-info">
        Issued at 2006-Aug-04 by Alexander
      </div>
      <div class="row-cell row-cell-links">
        <a href="#" onclick="return false;">permalink</a>,&amp;nbsp;
        <a href="#" onclick="return false;">edit</a>,&amp;nbsp;
        <a href="#" onclick="return false;">delete</a>
       </div>
    </div>
    <div class="pager">
      <div class="pager-links">
        <div class="pager-links-prev">
          <a href="#">newer articles</a>
        </div>
        <div class="pager-links-next">
          <a href="#">older articles</a>
        </div>
        <div class="pager-links-info">2 / 10</div>
      </div>
    </div>
  </div>
</div>

And styled with the following CSS rules:

.datalist { width: 40em; margin-left: auto; margin-right: auto;
            border: 2px solid #aaaaaa; background: #ffffee; 
            text-align: left; }
.title { margin: 0.7em; border: 1px solid #909090; 
           background: white; text-align: center; 
           letter-spacing: .1em; color: #303030;
           padding: .3em; }
.rows { }
.row { margin: 0.7em; border: 1px solid #909090;
       background: white; text-align: left; padding: .2em; }
.row-cell-caption { font-size: 1.2em; letter-spacing: .05em;
               font-weight: bold; color: #707070; }
.row-cell-description { font-size: .8em; color: #303030; }
.row-cell-info { font-size: .8em; color: #303030; text-align: right;
            margin-top: .2em; float: left; padding-left: .1em; }
.row-cell-links { font-size: .8em; color: #303030; text-align: right;
             margin-top: .2em; padding-right: .1em;
             border-top: 1px dotted gray; }
.pager { margin: 0.7em; border: 1px solid #909090;
       background: white; text-align: left; padding: .2em; }
.pager-links { font-size: .8em; color: #303030; margin: .2em;
               height: 1.5em; padding: .3em; }
.pager-links-prev { float: left; }
.pager-links-next { float: right; }
.pager-links-info { text-align: center; }

Let’s continue with creating HTML template and stylesheet file of the widget. The following widget HTML markup is created from the HTML code above by removing “item” elements (they will be created in runtime) and adding variables instead of static text:

DataList.html

<div class="datalist">
 <div class="title">
   ${title}
 </div>
 <div dojoAttachPoint="_rowsNode" class="rows">
  <!-- This code is commented because
        items will be created at runtime.
  <div class="row">
    ...
  </div>
  -->
 </div>
 <div class="pager">
  <div class="pager-links">
   <div class="pager-links-prev"><a href="#">${prevPageText}</a></div>
   <div class="pager-links-next"><a href="#">${nextPageText}</a></div>
   <div class="pager-links-info" dojoAttachPoint="_pagerLinksInfoNode">
    <!-- This section will be populated right after the list data
         is fetched from the server. -->
   </div>
  </div>
 </div>
</div>

The “dojoAttachPoint” attributes instruct the template parser to create corresponding widget properties that points to the HTML elements. These elements will be accessed and/or modified in widget methods.

The widget CSS file should not have styles for caption, description, info, and links. The styles for these elements should be defined on the page.

DataList.css

.datalist { width: 40em; margin-left: auto; margin-right: auto;
            border: 2px solid #aaaaaa; background: #ffffee; 
            text-align: left; }
.title { margin: 0.7em; border: 1px solid #909090; 
           background: white; text-align: center; 
           letter-spacing: .1em; color: #303030;
           padding: .3em; }
.rows { }
.row { margin: 0.7em; border: 1px solid #909090;
       background: white; text-align: left; padding: .2em; }
.pager { margin: 0.7em; border: 1px solid #909090;
       background: white; text-align: left; padding: .2em; }
.pager-links { font-size: .8em; color: #303030; margin: .2em;
               height: 1.5em; padding: .3em; }
.pager-links-prev { float: left; }
.pager-links-next { float: right; }
.pager-links-info { 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 dijit._Widget and dijit._Templated classes. In the following file pay attention to “templatePath” property - it specifies that the widget template is located in the same folder.

DataList.js

dojo.provide('user.DataList');
dojo.require('dijit._Widget');
dojo.require('dijit._Templated');
 
dojo.declare(
  'user.DataList',
  [dijit._Widget, dijit._Templated],
  {
    // current page
    page : 1,
 
    // total number of pages
    pages : 1,
 
    // DataList title
    title : 'DataList',
 
    // 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 : '',
 
    // where to look for template
    templatePath : new dojo._Url('', './DataList.html')
  }
);

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

<style type="text/css">
@import "DataList.css";
</style>
<script type="text/javascript" src="~/path/to/dojo.js"></script>
<script type="text/javascript" src="DataList.js"></script>
<script type="text/javascript">
dojo.require('dojo.parser');
dojo.addOnLoad(function() {
  dojo.parser.parse();
});
</script>
<!-- The following div with dojoType attribute specifies
  where an instance of the DataList widget will be rendered -->
<div dojoType="user.DataList" title="DataList Example"></div>

The “title” attribute of the <div dojoType="user.DataList" title="DataList Example"> tag demonstrates 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 step in 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="pager-links-prev">
  <a href="#" dojoAttachEvent="onclick:prevPage">${prevPageText}</a>
</div>
<div class="pager-links-next">
  <a href="#" dojoAttachEvent="onclick:nextPage">${nextPageText}</a>
</div>

And the handlers:

dojo.declare(
  'user.DataList',
  [dijit._Widget, dijit._Templated],
  {
    ...
 
    // 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.xhrGet({
          url: this.dataUrl.replace(/\{page\}/, this.page),
          handleAs: 'json',
          load: function(response, ioArgs) {
            _this.pages = response.pages;
            _this.render(response.items);
          }
        });
      }
    }
 
  }
);

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

Data rendering requires some explanation here. In trivial case all the data returned by provider is displayed in data list cell. But in a little more complex situations list of the displayed items is different from the list of items used in the row. For example, the row primary key should not be displayed, but should be used by the “edit” or “delete” operation links. To support this is the widget the “cells” property is introduced and it will be used in the render and renderRow methods as follows:

dojo.declare(
  'user.DataList',
  [dijit._Widget, dijit._Templated],
  {
    ...
    // visible row fields
    cells : '',
    ...
    render : function (items) {
      var rows = this._rowsNode;
      // clear current rows
      rows.innerHTML = '';
      // fields that should be rendered
      var cells = this.cells.split(',');
      // for each entry in items render fields
      // defined in the "cells" array
      for (var i = 0; i < items.length; i++) {
        var row = rows.appendChild(document.createElement('div'));
        dojo.addClass(row, 'row');
        for (var j = 0; j < cells.length; j++) {
          var cell = row.appendChild(document.createElement('div'));
          dojo.addClass(cell, 'row-cell');
          dojo.addClass(cell, 'row-cell-' + cells[j]);
          cell.appendChild(document.createTextNode(items[i][cells[j]]));
        }
        this.onRenderRow(row, items[i]);
      }
      this._pagerLinksInfoNode.innerHTML = this.page + ' / ' + this.pages;
    },
 
    // empty method that can be overwritten to customize
    // the row rendering
    onRenderRow : function (row, fields) {
    }
 
  }
});

Json data returned by the request has very simple structure. It is an object with two properties - pages and items. “pages” contains number of available pages and “items” is an array with objects, each object represents one row:

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

The DataList widget renders items into the divs. Without additional styles they will be displayed one after another without any decoration. To change this we can define several CSS rules for each data cell. Borders, margins, paddings and/or dozens of another presentation attributes, available in the CSS, can be changed in these rules. The name of such rule is created by prefixing field name with the “row-cell-” string. So, overwriting text color in the price column of the DataList can be achieved with the following CSS rule:

.row-cell-price {
    color: red;
}

Only the “Put all together” goal is left in the plan and it is the most interesting part of the widget developing.

Let’s customize caption, description, info and links using this approach. For this we need to add on the page the following style rules:

.row-cell-caption { font-size: 1.2em; letter-spacing: .05em;
               font-weight: bold; color: #707070; }
.row-cell-description { font-size: .8em; color: #303030; }
.row-cell-info { font-size: .8em; color: #303030; text-align: right;
            margin-top: .2em; float: left; padding-left: .1em; }
.row-cell-links { font-size: .8em; color: #303030; text-align: right;
             margin-top: .2em; padding-right: .1em;
             border-top: 1px dotted gray; }

You have already seen how the widget is placed on the page, and here is a more complex example:

<div
    id="articles"
    dojoType="user.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.

Our widget renders fields into text, but we need to display the permalink field as clickable link. To archive this we can decorate each row with JavaScript code. This code should be attached to the onRenderRow handler of the widget as follows:

dojo.addOnLoad(function() {
  ...
  var articles = dijit.byId('articles');
  articles.onRenderRow = function (row, fields) {
    var cell = row.appendChild(document.createElement('div'));
    dojo.addClass(cell, 'row-cell');
    dojo.addClass(cell, 'row-cell-links');
    cell.innerHTML = '<a href="' + fields.permalink
      + '">permalink</a>';
  };
});

And now we need to provide the widget with the data. For this we 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

set_include_path("/path/to/ZendFramework/library"); 
 
// Here can be database query or something that returns
// data similar to the following: 
$rowset = array(
 "pages" => 1,
 "items" =>
  array(
   array(
    "id" => 1,
    "caption" => "Creating new Dojo Widget",
    "description" => "This tutorial demonstrates ...",
    "info" => "Issued at 2006-Aug-23 by Alexander",
    "permalink" => "http://www.alex..."
   )
  )
 );
 
require_once 'Zend/Json.php';
echo Zend_Json::encode($rowset);

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

Code in this article was tested with Dojo Toolkit 1.4.1, Zend Framework 1.10.1. The article published in 2006 and revised in 2010.