It is time to start creating our own widget which is going to extend the default dijit Tree widget. To stay with our theme here we are going to create the following Javascript file ‘CheckboxTree.js’ in our tmpdir directory: ‘\Myserver\js\tmpdir\CheckboxTree.js’

Dojo recommends using your own namespace when creating new widgets and for this tutorial I picked the namespace ‘tmpdir’.

  1 dojo.provide("tmpdir.CheckBoxTree");
  2 dojo.require("dijit.Tree");

On line 1 we tell dojo what widget is included in this file and here you can already see the use of our own namespace ‘tmpdir’. On line 2 we include the original dijit tree widget because our tree and tree nodes will inherit from them and we are basically extending the classes dijit.Tree and dijit._TreeNode. The tree consists of two classes: 1) the tree and 2) its tree nodes, the tree acts as a tree node container so next we will declare both of them:

  4 dojo.declare( "tmpdir._CheckBoxTreeNode", dijit._TreeNode,
  5 {
  6 
  7 });
  8 
  9 dojo.declare( "tmpdir.CheckBoxTree", dijit.Tree,
 10 {
 11 
 12 });

Declare is a build-in function of dojo and on line 3 we declare the object _CheckboxTreeNode using the namespace tmpdir. Our tree node inherits from dijit._TreeNode. We do the same for our tree at line 9 for our tree object which inherits from dijit.Tree. Well belief it or not but you just created your own dijit widget, it’s not doing much but he, we have a widget.

To start using our newly created widget we have to go back to our index.html file and make some changes.

 14       dojo.registerModulePath("tmpdir","/js/tmpdir");
 15       dojo.require("dojo.data.ItemFileReadStore");
 16       dojo.require("tmpdir.CheckBoxTree");
 17 
 18       function myTree( domLocation ) {
 19         var store = new dojo.data.ItemFileReadStore( {url: "/js/datastore/countries.json"});
 20         var model = new dijit.tree.ForestStoreModel( {
 21                   store: store,
 22                   query: {type: 'continent'},
 23                   rootId: 'root',
 24                   rootLabel: 'Continents'
 25                   });
 26         var tree = new tmpdir.CheckBoxTree( {
 27                   model: model,
 28                   id: "MenuTree"
 29                   });

The first thing we need to do is to register our namespace and tell dojo were any files associated with the namespace tmpdir will be located, this can be an absolute or relative path. We will be using a path relative to our html directory. On line 16 we now include our file CheckboxTree.js and on line 26 we are actually going to use our widget. So instead of creating a dijit.Tree object we are now creating a tmpdir.CheckBoxTree object. Ok, that’s it for the index.html file, now lets continue with our widget an add some meaningful code.

  1 dojo.provide("tmpdir.CheckBoxTree");
  2 dojo.require("dijit.Tree");
  3 
  4 dojo.declare( "tmpdir._CheckBoxTreeNode", dijit._TreeNode,
  5 {
  6   _checkbox: null,
  7 
  8   _createCheckbox: function() {
  9     this._checkbox = dojo.doc.createElement('input');
 10     this._checkbox.type    = 'checkbox';
 11     this._checkbox.checked = false;
 12     dojo.place(this._checkbox, this.expandoNode, 'after');
 13   },
 14 
 15   postCreate: function() {
 16     this._createCheckbox();
 17     this.inherited( arguments );
 18   }
 19 });
 20 
 21 dojo.declare( "tmpdir.CheckBoxTree", dijit.Tree,
 22 {
 23   _onClick: function(/*Event*/ e) {
 24     var domElement = e.target;
 25     if(domElement.nodeName != 'INPUT') {
 26       return this.inherited( arguments );
 27     }
 28     var nodeWidget = dijit.getEnclosingWidget(domElement);
 29     if(!nodeWidget || !nodeWidget.isTreeNode){
 30       return;
 31     }
 32     nodeWidget._checkbox.checked ^ true;
 33   },
 34 
 35   _createTreeNode: function( args ) {
 36     return new tmpdir._CheckBoxTreeNode(args);
 37   }
 38 });

As we are going to create a checkbox on each node of the tree we need a local reference or handle for the checkbox, we declare it on line 6. Next we declare the method that will create the actual checkbox. On line 9 we tell dojo to create a DOM element ‘input’ and set the type to ‘checkbox’. Line 12 needs some explanation because we are asking dojo to place our checkbox after something called ‘expandoNode’. So the question is where did this.expandoNode come from?

Most dijit widgets inherit from dijit._Templated allowing those widget to use…. yes, templates. If we take a quick look at the html template for dijit._TreeNode located at \dojotoolkit\dijit\templates\TreeNode.html we find that a so called dojoAttachPoint is created with the name expandoNode. Every dojoAttachPoint can directly be referenced within the object like this.dojoAttachPointName which in our case is this.expandoNode so dojoAtachPoints act as placeholders in the DOM. So basically what we are doing on line 12 is to place our checkbox right after the +/- sign of a tree node.

On line 15 we declare our postCreate method which inherits from dijit._TreeNode and is called as part of a widget lifecycle. Every widget that inherits from dijit._Widget will automatically get the postCreate method which you can overwrite. On line 16 we call our own method to create the checkbox and on line 17 we call the parent postCreate method. The this.inherited(arguments) call is dojo shorthand for some nasty looking Javascript apply functionality. Ok, that’s it for our tree node and the creation of the checkbox, now lets move on to the tree itself.

Lets start at the bottom first, on line 35, the method _createTreeNode is called once from within the tree for every item in our Json data file. However, you need to understand that this only happens for tree nodes that need to be rendered. If you take a look at our first tree sample only 6 out of many nodes have been rendered. So until a node needs rendering it is not created and thus does not exist. This is important to keep in mind when we start sending events from our model to the tree.

The last part we need to do is to handle any click event associated with our checkbox. Again the _Onclick method is one of those we inherit from dijit.Tree. Because we are only interested in click events associated with our checkbox, line 24 first checks if the node name of the DOM element equals ‘input’ (see line 9 again). Next, on line 28, we try to locate the widget that actually encloses the DOM element and if it is a widget of type TreeNode we are in business, the only thing left to do is to toggle the checkbox checked attribute, line 32, and voila we have a working tree widget WITH checkboxes.

Go back to your browser and give it a try, after expanding some tree nodes your tree should look something like this:

Tree-step-3

If you do not see the checkboxes make sure you clear your browser cache, browsers have a tendency to cache a lot of stuff in the background.

And oh BTW, the page title is correct now…. This concludes step 3, you may be surprised to know that most of the work we needed to do on the tree widget and its nodes is almost done. In the next step,STEP 4, we are going to add custom events and do some lite plumbing so the tree can interact with the model. From thereon is all about the model.