Step 3 – Adding Checkboxes

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.

9 Replies to “Step 3 – Adding Checkboxes”

  1. Hi! Thanks for a great tutorial! I was wondering if you have tried this code with Dojo libs loaded from CDN? I can’t make it work due to the fact that it does not like cross-domain overloading of Dojo dijits. Any ideas on how to solve this problem? Thanks in advance!

    1. @tbs

      Yes I have, the demo running on my site is loading all the Dojo/Dijit stuff strait from Google. In order to make it work you have to do two things:
      1: Reference and load dojo.xd.js from Google or any other CDN.
      2: Build a cross-domain version of the CheckBoxTree.js

      To load Dojo from Google use the following script (this is an example an already old, so check with Google for the correct version and location):

      (script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js")(/script)
      (script type="text/javascript" src="/js/tmpdir/CheckBoxTree.xd.js")(/script)

      Next build your cross-domain version of CheckBoxTree.js to create CheckBoxTree.xd.js. The XD profile I used looks like:

      dependencies = {
      stripConsole: "normal",
      layers: [
      {
      name: "../../tmpdir/CheckBoxTree.xd.js",
      dependencies: [
      "tmpdir.CheckboxTree"
      ]
      }
      ],

      prefixes: [
      [ "dijit", "../dijit" ],
      [ "tmpdir", "../../tmpdir" ]
      ]
      };

      I have to admit, it’s been a while since I did this myself but Google/Bing is your friend. If every thing goes according to plan you will end up with CheckBoxTree.xd.js of about 111 KB.

      Hope this helps.

  2. Hi again! Thanks for your reply. I just managed to solve the problem and I might have a simpler solution. All I had to do is to add following two (top) lines to djConfig variable:

    var djConfig = {
    modulePaths: {“temp”: “js/temp/”},
    baseUrl:’./’,
    parseOnLoad: true,
    etc…
    }

    instead of this:

    dojo.registerModulePath(“tmpdir”,”/js/tmpdir”);

    It seems like the Dojo guys also advise using djConfig instead of registerModulePath:

    http://docs.dojocampus.org/dojo/registerModulePath

    Anyways, thanks for your reply, thanks for a good tutorial, and I hope I could also contribute just a little bit. 🙂

    Oh, btw, it might seem like you have a little type on line 23 on this page. It should be:

    23 _onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e) {

    instead of:

    23 _onClick: function(/*Event*/ e) {

    Otherwise you get node widget as an event and get some odd errors when you try to get e.target on line 24.

    1. The advantage of a properly ‘compiled’ cross domain version of CheckBoxTree would be that people won’t load any core Dojo/Dijit stuff from your site but from the CDN instead. In addition, the cross-domain version is a compressed version using a lot less bandwidth. Anyway I’m glad you found a way to make it work.

    2. With regards to line 23, the original post was based on Dojo version 1.3.x. If you take a look a “Step 8 – Dojo Version 1.4” you will find all the differences and changes I had to make in order to make it work for version 1.4 (including line 23 🙂 )

  3. All I have left to figure out is how to loop through the tree (or store) and get a list of checked and unchecked nodes. Do you happen to have a piece of code that does this? Don’t worry if you don’t, I will just have a look at that tomorrow. 🙂

    1. Take a closer look at the method ‘_validateStore’. It loops true all entries in the datastore which of course hold the actual state of any checkbox.

      Good luck.

  4. first of all thank you very much for the well documented code and explanation. Actually I am a new guy in Dojo world. I am trying to implement this tree since I need it in my project but unfortunately it does’t work. i used the code exactly as you have mentioned but keeps arise the following error:
    can’t load could’t load tmpdir.CheckBoxTree i used the registerModule also i used the modulePaths in the djconfig but the same problem.
    please i need your help.

Leave a Reply

Your email address will not be published. Required fields are marked *