Lets go to the next phase of our little project and take a look at our original requirement d:

I want to be able to specify the initial state (checked/unchecked) of a checkbox but I don’t want to be forced having to do this for every checkbox. For example, if all checkboxes have a default state of ‘checked’ with the exception of only a few, I want to be able to define the default state once and only deal with the exceptions.

I’m going to implement this requirement in several smaller steps, first we need something that could tell us what the initial state of the checkbox needs to be. The most obvious place is of course our Json source file. I’m going to introduce a new attribute called the checkbox Identifier or checkboxIdent for short and add that to our file. Create a new file called \html\js\datastore\countriesChk.json

  1 { identifier: 'name',
  2   label: 'name',
  3   items: [
  4      { name:'Africa', type:'continent', checkbox: true,
  5          children:[{_reference:'Egypt'}, {_reference:'Kenya'}, {_reference:'Sudan'}] },
  6      { name:'Egypt', type:'country', checkbox: true },
  7      { name:'Kenya', type:'country', checkbox: true,
  8          children:[{_reference:'Nairobi'}, {_reference:'Mombasa'}] },
  9      { name:'Nairobi', type:'city', checkbox: true },
 10      { name:'Mombasa', type:'city', checkbox: true },
 11      { name:'Sudan', type:'country', checkbox: true,
 12          children:{_reference:'Khartoum'} },
 

Each item in our data file gets an additional “key : value” pair, the key being ‘checkbox’ and the value could be either true or false. Not to be as flexible as a hardwood door I want to make the key configurable just because you never know. To do so, I’m going to introduce two attributes to our model, one which will define the key and the other the default state in case an item in our Json file doesn’t have the key specified. (ie: the checkbox default value).

To read the initial state of our checkboxes we also need a method that can get the new “key : value” pair from our store. Go ahead and make the following changes to our model and the CheckBoxTreeNode:

  1 dojo.provide("tmpdir.CheckBoxTree");
  2 dojo.provide("tmpdir.CheckBoxStoreModel");
  3 dojo.require("dijit.Tree");
  4 
  5 dojo.declare( "tmpdir.CheckBoxStoreModel", dijit.tree.ForestStoreModel,
  6 {
  7   checkboxIdent: "checkbox",
  8   checkboxState: false,
  9 
 10   updateCheckbox: function(storeItem, newState ) {
 11   },
 12 
 13   getCheckboxState: function(/*dojo.data.Item*/ storeItem) {
 14     var currState = undefined;
 15 
 16     if ( storeItem == this.root ) {
 17       if( typeof(storeItem.checkbox) == "undefined" ) {
 18         currState = this.root.checkbox = this.checkboxState;
 19       } else {
 20         currState = this.root.checkbox;
 21       }
 22     } else {
 23       currState = this.store.getValue(storeItem, this.checkboxIdent);
 24     }
 25     return currState
 26   },
 27 
 28   onCheckboxChange: function( storeItem ) {
 29   }
 30 });
 31 
 32 dojo.declare( "tmpdir._CheckBoxTreeNode", dijit._TreeNode,
 33 {
 34   _checkbox: null,
 35 
 36   _createCheckbox: function() {
 37     var  currState = this.tree.model.getCheckboxState( this.item );
 38     if( currState !== undefined ) {
 39       this._checkbox = dojo.doc.createElement('input');
 40       this._checkbox.type    = 'checkbox';
 41       this._checkbox.checked = currState;
 42       dojo.place(this._checkbox, this.expandoNode, 'after');
 43     }
 44   },
 45 
 46   postCreate: function() {
 47     this._createCheckbox();
 48     this.inherited( arguments );
 49   }
 50 });

Starting on line 7 we introduce the new attributes checkboxIdent with the default value of “checkbox” and checkboxState with its default value of false. Both attribute can be used as configurable arguments when we create our model (more on that later).

On line 13 we declare our getCheckboxState method with only one argument being a store item. On line 16 we check if the store item passed to us isn’t our root element of our tree. Remember, as I said before the tree root does not represent any item in our data store and therefore we need to treat it differently. If it is a true store item we will fetch its initial checkbox state from the store using our new attribute checkboxIdent at line 23.

Now that we have the capability to fetch the checkbox state from our file we are going to use it when we create our checkbox on the tree nodes. On line 37 we get the checkbox state followed by a single check to see if we got something meaningful back. Undefined in this context means we were unable to locate the requested key value pair for this item in our data store. So any item in our Json file that doesn’t have “checkbox : true/false” specified will return the state undefined.

This behavior immediately satisfies our original requirement b):

It must be able to support both trees with and without checkboxes so I only need one widget going forward.

Cool, one simple check….

That BTW that was the last change we had to make to our CheckBoxTreeNode, we are done with that one. Ok, go back to your browser and reload your page, see what happens. If you didn’t jump ahead your tree should look something like this:

Step-5 Tree without checkbox

All the checkboxes are gone with the exception of our tree root. Any idea why? Well we haven’t updated our index.html file yet telling the store to use our new file coutriesChk.json.

 15     function myTree( domLocation ) {
 16       var store = new dojo.data.ItemFileWriteStore( {
 17               url: "/js/datastore/countries.json"
 18               });
 19       var model = new tmpdir.CheckBoxStoreModel( {
 20               store: store,
 21               query: {type: 'continent'},
 22               rootId: 'root',
 23               rootLabel: 'Continents',
 24               });

In our index.html file update the URL on line 17, save it and reload your page again. If you did set the checkboxes all to true your tree should look like:

Step-5 Tree with checkbox true

Ok, go play around with the new countriesChk.json file, change some of the checkbox initial states and/or remove the key value pair completely for several items. The last thing I want to take care of in this step is second part of our requirement, just having to specify the exceptions:

I want to be able to specify the initial state (checked/unchecked) of a checkbox but I don’t want to be forced having to do this for every checkbox. For example, if all checkboxes have a default state of ‘checked’ with the exception of only a few, I want to be able to define the default state once and only deal with the exceptions.

I’m going to introduce another attribute for our model called checkboxAll with a default value of true. In addition I want to be able to specify if the tree root will get a checkbox, so one more attribute: checkboxRoot and again we’ll give it a default value of true.

  5 dojo.declare( "tmpdir.CheckBoxStoreModel", dijit.tree.ForestStoreModel,
  6 {
  7   checkboxIdent: "checkbox",
  8   checkboxAll: true,
  9   checkboxRoot: true,
 10   checkboxState: false,
 11 
 12   updateCheckbox: function(storeItem, newState ) {
 13   },
 14 
 15   getCheckboxState: function(/*dojo.data.Item*/ storeItem) {
 16     var currState = undefined;
 17 
 18     if ( storeItem == this.root ) {
 19       if( typeof(storeItem.checkbox) == "undefined" ) {
 20         this.root.checkbox = undefined;
 21         if( this.checkboxRoot ) {
 22           currState = this.root.checkbox = this.checkboxState;
 23         }
 24       } else {
 25         currState = this.root.checkbox;
 26       }
 27     } else {
 28       currState = this.store.getValue(storeItem, this.checkboxIdent);
 29       if( currState == undefined && this.checkboxAll) {
 30         currState = this.checkboxState;
 31       }
 32     }
 33     return currState
 34   },
 35 
 36   onCheckboxChange: function( storeItem ) {
 37   }
 38 });

On line 8 and 9 we define the new attributes checkboxAll and checkboxRoot both with their default value of true. Next on line 21 and 22 we determine if the tree root needs a checkbox. On line 29 we check if every store item requires a checkbox in case the key value pair was not specified in the Json file. If set to true we will return the default state checkboxState. Remember, the CheckBoxTreeNode will create a checkbox as long as the value returned is not undefined.

So how can we use all these newly created attributes? Good question, lets take a look at our index.html file again.

 15     function myTree( domLocation ) {
 16       var store = new dojo.data.ItemFileWriteStore( {
 17               url: "/js/datastore/countries.json"
 18               });
 19       var model = new tmpdir.CheckBoxStoreModel( {
 20               store: store,
 21               query: {type: 'continent'},
 22               rootId: 'root',
 23               rootLabel: 'Continents',
 24               checkboxAll: true,
 25               checkboxRoot: false,
 26               checkboxState: true
 27               });

Just to demonstrate the power of using attributes I changed my URL on the store back to the original countries.json file and added all three checkbox related attributes we just created. Keep in mind, all these checkbox attributes have default values and therefore are optional. Go ahead and create some different scenarios, change the attribute values, try some different combinations, switch back and forth between your Json files and change their content.

That concludes step 6 of our project. In STEP 7 we are going to build the Parent-Child relationship.