Creating a Dojo dijit.Tree with checkboxes
November 21, 2008 – 12:11 pm Tags: JavaScript, source codeDojo provides a useful component called dijit.Tree, which is basically a quite typical tree component. However, it doesn’t do much out of the box, and I needed it to make some tree nodes selectable with checkboxes for my Zend Framework based packageizer script. While Zend Framework has a Zend_Dojo component, it doesn’t quite do trees the way I want yet.
Let’s see how I made the tree play nice with checkboxes and some ajax tricks.
The basic idea
If you’ve checked out the packageizer, you probably already know most of this:
I wanted the tree to load its nodes from the PHP script, separately for each leaf. I also wanted to have “folders” and “files” - folders would contain things, and files would have a checkbox that can be checked to select it. I also needed to be able to track the checked items, so the script would know what files to put in the package.
The dijit.Tree is basically two main components: dijit.tree and dijit._TreeNode. Typically you’d also need a store, like dojox.data.QueryReadStore in my case, and a model, like dijit.tree.ForestStoreModel. By extending some of these, we can easily modify the tree to fit our needs.
Getting to it
You can find the complete source code here.
dijit._TreeNode
Let’s first take a look at the dijit._TreeNode class, which represents each node in a tree. We can easily modify it to contain a checkbox, if certain requirements are met:
dojo.declare('CU.dojo._ChkTreeNode', dijit._TreeNode, { setLabelNode: function(label) { if(this.item.root || this.tree.model.store.getValue(this.item, 'type') != 'class') return this.inherited(arguments); var chk = dojo.doc.createElement('input'); chk.type = 'checkbox'; this.labelNode.innerHTML = ''; dojo.place(chk, this.expandoNode, 'after'); this.labelNode.appendChild(dojo.doc.createTextNode(label)); this.checkNode = chk; this.expandoNodeText.parentNode.removeChild(this.expandoNodeText); } });
The above code checks if the item is of type “class”, and inserts a checkbox after the expando (the +/- sign). The setLabelNode function gets automatically called when the tree node gets created.
But why class? This is because the data received from the PHP script assigns type as “class” for all classes and “package” for packages, which can contain classes or other packages. If you wanted to add checkboxes to all nodes, you can simply remove the if clause.
dijit.Tree
This is going to be a bit longer, so I’m just going to explain it and show some snippets. You can get the complete code and follow from there.
In the declaration for CU.dojo.ChkTree, we can see the _createTreeNode function. This is a function in dijit.Tree, which gets called when the code needs to create a new tree node, so this is the place where we need to return our shiny new ChkTreeNode.
The getIconClass function determines the CSS class name for the icon of the tree node. This is modified to output “package” or “class” so we can use CSS styles to modify how the icons look. Same is done for the getLabelClass function.
The onNodeChecked and onNodeUnchecked function’s purprose is to serve as a base which you can dojo.connect to, or just simply override to get a notification when a node is checked or unchecked.
onClick: function(item, node) { if(item.root || this.model.store.getValue(item, 'type') == 'package') { this._onExpandoClick({ node: node }); } else { if(!node.checkNode) return; if(this._clickTarget && this._clickTarget.nodeName != 'INPUT') node.checkNode.checked = !node.checkNode.checked; if(node.checkNode.checked) { this.onNodeChecked(node); } else { this.onNodeUnchecked(node); } } },
The onClick is a bit more tricky. It gets called when any node is clicked, but we want to have a bit different behavior depending on the node type. If it’s a package, we want it to be expanded or minimized on click, but if it’s a class, the checkbox should be toggled.
The first block checks if it’s a package, and calls the _onExpandoClick function, which toggles the node. If it’s a class, we check if it actually has a checkbox node in it or not. Then, we need to check if what was clicked was the checkbox: If the checkbox was the click target, it was already toggled, and toggling it again would make it impossible to use. Finally, calls are made to the related event functions.
And lastly in the ChkTree, we have _onClick, which is needed for the checkbox to function correctly. Unless we have this function in, every time we click on the checkbox, it would focus the label, and it would not be possible to check it by clicking the checkbox directly, only by clicking the label and thus invoking the onClick handler for it.
dijit.tree.ForestStoreModel
And the last extended class, CU.dojo.QueryStoreModel, is required to add some custom behavior to the store so the tree works correctly. We modify getChildren to use some custom logic to fetch the child nodes from the store, as the backend PHP code requires some additional parameters to be able to correctly determine the nodes.
We also override mayHaveChildren to allow children for packages but not for classes.
Usage
Using this class is very similar to how you would normally use a dijit.Tree. Here’s a simplified example from the packageizer:
var store = new dojox.data.QueryReadStore({ url: '/pack/1.6' }); var model = new CU.dojo.QueryStoreModel({ store: store, query: { package: '', format: 'json' }, rootId: 'treeRoot', rootLabel: 'Packages', childrenAttrs: 'children' }, 'store'); var tree = new CU.dojo.ChkTree({ model: model }, 'tree'); dojo.connect(tree, 'onNodeUnchecked', function(node) { alert('A node was unchecked!'); }); dojo.connect(tree, 'onNodeChecked', function(node) { alert('A node was checked!'); }); tree.startup();
Here’s an example of the JSON sent by the PHP backend:
{ "numRows":3, "items":[ {"name":"Zend_Auth_Storage_Exception","label":"Zend_Auth_Storage_Exception","parent":"Zend_Auth_Storage","type":"class"}, {"name":"Zend_Auth_Storage_NonPersistent","label":"Zend_Auth_Storage_NonPersistent","parent":"Zend_Auth_Storage","type":"class"}, {"name":"Zend_Auth_Storage_Session","label":"Zend_Auth_Storage_Session","parent":"Zend_Auth_Storage","type":"class"} ], "identity":"name" }
In closing
In the end, this required less code than I expected in the beginning. I’ve done something similar using the Yahoo UI library, and it was a bit more complex. What Dojo lacks in documentation (at the moment of writing this), it has in design and flexibility.
You can see a live example of the tree here.
Hope you found this educational or useful =)












15 Responses to “Creating a Dojo dijit.Tree with checkboxes”
Add the checkboxes to the packages too.. and use it to allow you to select all the child classes..
Sometimes you just want the whole bit.. like Zend_Mail.. it’s standalone, but selecting all the classes in your tree to find that out is a lot more troublesome then just selecting the Zend_Mail package and finding it out that way
By Harro on Nov 21, 2008
Oh.. and could you also include .xml files in the download?
When I select Zend_Locale I don’t get all the xml files in the Zend/Locale/Data
By Harro on Nov 21, 2008
You’re commenting packageizer in the wrong place, this post is about dijit.Tree
Anyway - I’ve read the feedback and when I have time to work on it, that package checking will be amongst the other improvements =)
XML files may be a bit trickier… I will need to see if there’s some good way to determine any external non-PHP files a class requires.
By Jani Hartikainen on Nov 21, 2008
I have a method,but it can’t work.Could you help me?
–code–
dojo.extend(dijit.Tree,{
_createTreeNode: function(/*Object*/ args){
args= dojo.mixin(args,{
setLabelNode: function(label){
this.labelNode.innerHTML = “”;
var id = continentStore.getIdentity(this.item);
var input=document.createElement(’input’);
input.setAttribute(’type’,'checkbox’);
input.setAttribute(’name’,'groupid[]’);
input.setAttribute(’id’,'groupid_’+id);
input.setAttribute(’value’,id);
//input.setAttribute(’checked’,'checked’);
this.labelNode.appendChild(input);
this.labelNode.appendChild(dojo.doc.createTextNode(label));
}
}
);
return new dijit._TreeNode(args);
}
});
By xletian on Dec 8, 2008
Could you please provide the code for the PHP script? Thanks
By George on Jan 14, 2009
Thanks.I have solved it.because of below code in function of _onClick.
–code–
dojo.stopEvent(e);
–code–
By xletian on Jan 14, 2009
George: the code is available at http://codeutopia.net/code/packageizer/trunk/
By Jani Hartikainen on Jan 14, 2009
Jani, just wanted to let you know that the live example does not work anymore…
regards,
Matthias
By Matthias Zeis on Jul 1, 2009
Live Examples not working .. please fix that.
Thanks…
By Ramesh Krihsnan on Jul 17, 2009
The live example is broken, again.
By Tim on Aug 14, 2009
The live example is back up. The script was down for a while due to some hosting change issues.
By Jani Hartikainen on Aug 21, 2009
Dear Jani,
This tree is exactly what I need for my project, thank you a lot for providing it. I start in Zend and I’ve no experience at all with Dojo so it’s quite difficult for me to use it.
I’ve tried to install you example on my server, I can select the 1.9 library (adding a zend lib in data/1.9) but when I click on it I’ve lot of errors I’m unable to fix.
My question is:
How to (and it is possible to) use this tree on a query result ? I’ve a query with return a sort of tree (family, subfamily, item) and I absolutely need the ability to set a value (a date) for selected values in the tree.
How can it be done ? Most people need this tree checkbox for database items, so probably it would be a great example.
Also as requested, the possibility to select all items in a folder would be great.
Could someone help ?
Would it be possible to create this tree as a “package” or “library” to include in Zend and pass to it the values in a desired format ?
Thank you for helping.
By Bedford on Nov 26, 2009
You probably shouldn’t use the code from packageizer directly if you just want the tree. The code in it is specialized for this specific case.
I may post something in the future about creating a similar tree from a database result though.
By Jani Hartikainen on Nov 26, 2009
Hi , Can you please tell me why i am getting below error -
Could not load CU.dojo.ChkTree
Please help.
Ashish
By Ashish Soni on Dec 14, 2009