Displaying and Styling a Read-only Dojo Tree Control

In this tutorial, we are going to be using the Dojo toolkit – a popular JavaScript tookit shipped with the IBM Feature Pack for Web 2.0 and Mobile. Specifically, we are going to be using the dijit.Tree class from the Dojo toolkit to display a simple Dojo read-only tree that can display information from a hierarchy of data. We’ll also style the tree, showing how to style different levels of the tree differently.

We’ll start out with a basic JavaScript object that contains some data we want to display. It’s hierarchical in nature – arrays inside arrays. In this example, we assume we are running an online retailer, and we want to display all the customers we have, and for each, all the orders they have made in the last month, then within those, each of the items in the order. Our object looks like this:

var customerOrderHistory = [
    {
        "name": "Fred Bloggs",
        "customerNum": 23,
        "orders": [
            {
                "orderNum": "0001",
                "items": [
                    {
                        "description": "Gone with the Wind (DVD)",
                        "itemNum": 56
                    },
                    {
                        "description": "Oxford English Dictionary",
                        "itemNum": 78
                    }
                ]
            },
            {
                "orderNum": "0002",
                "items": [
                    {
                        "description": "Whittaker's Alamanac",
                        "itemNum": 90
                    }
                ]
            }
        ]
    },
    {
        "name": "David Smith",
        "customerNum": 45,
        "orders": [
            {
                "orderNum": "0003",
                "items": [
                    {
                        "description": "King James Bible",
                        "itemNum": 112
                    }
                ]
            }
        ]
    }
];

Before we can create a Dojo Tree to display this data, we need to create a Tree Model. In our case, we’re going to use the dijit.Tree.ForestStoreModel to do this, as our tree has multiple roots (customers).

In turn, in order to create this tree model, we in turn need to get place our items into a Dojo data store (we’re going to use the dojo.data.ItemFileReadStore, as it’s well placed for handling in-memory data). To create this data store, we first need a data structure where each node and leaf in the tree has a consistent field name for its identifier (the internal representation of that node or leaf) – which must be unique across the entire tree – and a consistent field name for its label (what will be displayed on the screen for that node or leaf). Each node also needs to have a consistently named array that contains its child elements. Therefore, we need to write a data restructuring function. Our looks like this:

_restructureCustomerData = function(data) {
        var newData = dojo.map(data, dojo.hitch(this, function(customer) {
            var newCustomer = {};

            newCustomer.id = customer.customerNum;
            newCustomer.name = customer.name;

            newCustomer.children = dojo.map(customer.orders, dojo.hitch(this, function(idPrefix, order) {
		var newOrder = {};

		newOrder.id = idPrefix + "_" + order.orderNum;
		newOrder.name = order.orderNum;

		newOrder.children = dojo.map(order.items, dojo.hitch(this, function(idPrefix, item) {
		    var newItem = {};

		    newItem.id = idPrefix + "_" + item.itemNum;
		    newItem.name = item.description;

		    return newItem;
		    }, newOrder.id));

		return newOrder;
		}, newCustomer.id));

            return newCustomer;
        }));

        return newData;
    }

This will walk through the object tree of our initial data, and at each level will use the dojo.map function to create an entirely new resultant object tree, that consistently at each level uses the field names id (for the identifier) and name (for the label). Each non-leaf node also has a field called children that contains the child elements. For illustration, the resultant object looks like this:

[
    {
        "id": 23,
        "name": "Fred Bloggs",
        "children": [
            {
                "id": "23_0001",
                "name": "0001",
                "children": [
                    {
                        "id": "23_0001_56",
                        "name": "Gone with the Wind (DVD)"
                    },
                    {
                        "id": "23_0001_78",
                        "name": "Oxford English Dictionary"
                    }
                ]
            },
            {
                "id": "23_0002",
                "name": "0002",
                "children": [
                    {
                        "id": "23_0002_90",
                        "name": "Whittaker's Alamanac"
                    }
                ]
            }
        ]
    },
    {
        "id": 45,
        "name": "David Smith",
        "children": [
            {
                "id": "45_0003",
                "name": "0003",
                "children": [
                    {
                        "id": "45_0003_112",
                        "name": "King James Bible"
                    }
                ]
            }
        ]
    }
]

We can now use this object as the input to the creation of a data store:

var store = new dojo.data.ItemFileReadStore({
        data: {
            "identifier": "id",
            "label": "name",
            "items": restructuredData
        }
    });

And then in turn a store model:

var treeModel = new dijit.tree.ForestStoreModel({
        store: store,
        rootId: "root",
        rootLabel: "Customers",
        childrenAttrs: ["children"]
    });

Assuming that have an HTML div on our page with an id of “_treeHolder”, we can now create the tree and attach it to that empty div:

this._tree = new dijit.Tree({model: treeModel, showRoot: false}, dojo.byId("_treeHolder"));

The tree will now display successfully. With some items expanded, it looks like this:

Applying styles to the tree

There are many ways we can apply CSS styling to the tree. However, a category of advanced techniques involves overriding the tree class itself to add different CSS classes to different levels of the tree, therefore enabling different styles at different depths. We start out by altering the _restructureCustomerData function slightly to insert metadata about each object, specifically which CSS class we want to apply to each level in the tree:

 _restructureCustomerData = function(data) {
        var newData = dojo.map(data, dojo.hitch(this, function(customer) {
            var newCustomer = {};

            newCustomer.id = customer.customerNum;
            newCustomer.name = customer.name;
	    newCustomer.cssClass = "customer";

            newCustomer.children = dojo.map(customer.orders, dojo.hitch(this, function(idPrefix, order) {
		var newOrder = {};

		newOrder.id = idPrefix + "_" + order.orderNum;
		newOrder.name = order.orderNum;
	        newOrder.cssClass = "order";

		newOrder.children = dojo.map(order.items, dojo.hitch(this, function(idPrefix, item) {
		    var newItem = {};

		    newItem.id = idPrefix + "_" + item.itemNum;
		    newItem.name = item.description;
                    newItem.cssClass = "description";

		    return newItem;
		    }, newOrder.id));

		return newOrder;
		}, newCustomer.id));

            return newCustomer;
        }));

        return newData;
    }

We now extend the dijit.Tree class itself to create our own tree class, overriding the getRowClass() function. This allows us to assign a custom CSS class to each row, based on the data we’ve just inserted into the data store:

    dojo.declare("ibm.dijit.examples.LevelSensitiveTree", [ dijit.Tree ], {
	getRowClass: function(item, opened) {
	    if(item.id !== "root") {
		return this.model.store.getValue(item, "cssClass");
            }
	}
    });

We can then use this new tree class in the same way as the regular dijit.Tree class. The resultant tree looks like this:

In this article you have hopefully learnt some practical lessons on how to use and style Dojo trees based on raw data.

Writing a simple, self contained tab container Dijit in Dojo

Over the last few weeks, I’ve been working with a new team and have been putting together some versatile dijits that are to be used throughout the UI. Today I spent some time working on an idea for a tab container which requires little or no customisation / interaction to get set up and running.

Additional Comment: The reason why I have done this over using the built in Dojo TabContainer is that I find it to be a bit ‘over the top’ for simple use as a plain tab container. I get faced with steep design guidelines by clients and even STEEPER browser requirements (such asIE6) and as a result, I find that it’s important to keep the memory / processor useage to a minimum and ensure that the fewest number of dom nodes are created. Thus, my ‘lite’ tabContainer was born!

I’ve spent the last hour or two re-writing a clean room version which I’d like to show to you all to demonstrate how easy it is to style and manipulate dom elements once you have them on the page.

Click to skip to the Demo Page

The idea was simple…
Get a tab container on a page, with tab switching, tab change events and a way to set which tab is shown externally.
The main requirement I gave myself what that it MUST be simple and self contained. In the past I’ve added tabs and panes to the page separately and then connected their events, but this proves fiddly and repetitive, so I’ve worked all of the variables into the dijit attributes, settable via it’s declarative marking! So…

What’s in the template…
…Not much

<div class="tabContainer">
	<ul dojoAttachPoint="_listNode"></ul>
	<div dojoAttachPoint="containerNode" class="content"></div>
</div>

An outer div with a class to restrict my CSS calls easily, a list to hold the tabs and a ‘content’ div with the magic attach point ‘containerNode’. As mentioned before, this is used throughout the dijit framework, and is where the innerHTML of your dijit declaration is copied to when the dijit is parsed. So this is the div into which our tab pane content will be put.

Now for the declaration…

<div dojoType="tom.dijit.TabContainer">
	<div tab="tab1" label="Tab One" selected="true">Content One</div>
	<div tab="tab2" label="Tab Two">Content Two</div>
	<div tab="tab3" label="Tab Three">Content Three</div>
</div>

As you can see, I’m putting using the ‘tab’ and ‘label’ attrs of the inner divs to define my parameters. The ‘tab’ param is the name that the tabContainer dijit will refer to the pane by, these need to be unique within each tabContainer level. The ‘label’ param is simply picked up and placed as the link text inside the tab and is the text that the user will see. And that’s all there is to it from a html point of view. This simple model allows n layer nesting of tab containers and makes it all very simple.

So how does that work…
Well, during the post create of the tabContainer dijit, I run a dojo query for all immediate children on my containerNode with the attribute ‘tab’. For each of these, I get hold of it’s tab and label attributes and create a list item and link for each result in the list node. I then add this info along with attach points to both the pane node and the tab node to a dijit level object and finally, I check to see if the pane has ‘selected==true’ and apply the selected class to it’s tab accordingly. If a pane is not ‘selected’, I hide it using a simple ‘display:none’ class named hidden.

postCreate : function() {
		this.inherited(arguments);
		
		//look for divs with a tab attr inside my container node!
		dojo.query("> div[tab]", this.containerNode).forEach(dojo.hitch(this, function(thisNode){
			//start building up our tab object
			var tabObj = {	"paneNode" : thisNode,
							"label" : dojo.attr(thisNode, "label") ? dojo.attr(thisNode, "label") : "Undefined" };
			//get the tab name, we'll need this!
			var tabName = dojo.attr(thisNode, "tab");
			
			//add a list item and an anchor tag to the list
			var tabItem = dojo.create("li", {"class":"tab"}, this._listNode);
			var tabLink = dojo.create("a", {"innerHTML": tabObj.label, "href":"javascript:;", "tab":tabName}, tabItem);
			tabObj.tabNode = tabItem;
			
			//connect the onclick event of our new link
			dojo.connect(tabLink, "onclick", this, "_onTabLinkClick");
			
			//now add our tabObj to the dijit level tabList so we can 
			//get to it later
			this._tabList[tabName] = tabObj;
			
			//get hold of the selected attr, to see if it should
			//be initially visible - last one found will win this battle
			var selected = dojo.attr(thisNode, "selected");
			
			if (!selected) {
				//not selected, so adding hidden class
				//(hidden class defined in global css as display:none)
				dojo.addClass(thisNode, "hidden");
			}
			else {
				//this ones selected, so put the name into our selectedTab var
				this._selectedTab = tabName;
				//add a 'selected' class to our tab list item
				dojo.addClass(tabItem, "selected");
			}
		}));		
	}

The result of all of this, is a very plain dom tree with all the elements we need to flesh out via CSS a lightweight, customisable tabContainer.

For the purposes of this demo, I have added a bounding border and a wrapper div to show the tabContainer’s containment.

So with a little bit of CSS effort, you can now transform this shell to any number of tabContainer layouts. For now, here’s a screenshot and a snippet of a small bit of CSS to give you a feel (bare in mind I spent 2 min on this CSS).
I hope to add another post in a few days to go over how to style the dijit and use the tabContainer’s events and functions to interact with a page controller (page level javascript).

Once again, I’ve used dashed borders to show where boundaries lie. The basic CSS is shown below

div.tabContainer > ul {
	list-style: none;
	padding: 0;
	margin: 0;
	height: 25px;
}

div.tabContainer > ul > li {
	float: left;
	height: 15px;
	padding: 5px;
	margin: 0px 5px;
	background: black;
}

div.tabContainer > ul > li > a {
	text-decoration: none;
	color: #EBEBED;
}

div.tabContainer > ul > li:hover,
div.tabContainer > ul > li:hover a {
	background: #EBEBED;
	color: #black;
}

div.tabContainer > ul > li.selected a {
	color: #729fcf;
	font-weight: bold;
}

Click the link to go take a look at a working demo (which I’ll upload shortly after completing this post!)
TabContainer Demo Page

TabContainer Full JS Source Code

Enjoy!

Some IE6 ‘niggles’….

Been working with IE6 recently, (yes, the browser even MS has shunned) and have amazingly come across some issues!

I thought I would share some of them with anyone who cares to read this blog, as you may well come across them too!

Concatenated classes in CSS selectors? No Thankyou says IE6!

Spent a while the other day trying to prove that it’s “really easy” to skin dojo dijits for use on a clients system recently, and wrote some efficient CSS to enable the application of different classes for size, colour and type etc… Trouble is, I couldn’t get the bloody thing to work! Tried everything, but alas, hit the IE6 CSS selector brick wall.  Turns out, that IE6 only sees the last class you apply! So something like the following:

.dijitButton.Big.Red {
	//make this button big and red
}

.dijitButton.Small.Red {
	//make this button small and red
}

Kept making all of my “Red” buttons go small, no matter what code I put in the first CSS section. Now this may not seem like a big problem in this case, but take a look at the following:

.dijitButton.Big.Red {
	background: red;
}

.dijitTextBox.Red {
	color: red;
}

In this case, when you apply this CSS to your IE6 page, it cannot tell the different between the two selectors. All it sees is the class “Red” and will match both rules to anything with this class. The result, a useless dijitTextBox with a red background AND a red foreground!
The way around this seems to be to use large, unruly, nasty, horrible, unmaintainable, slow, inefficient combined classes… ie.

//when mouse out
<div class="outerDivBlue">
	<span class="innerSpanLarge"></span>
</div>

//when mouse over (after some JS)
<div class="outerDivBlueHover">
	<span class="innerSpanLarge"></span>
</div>

Thus meaning that you CSS needs to cover every base when defining selectors. ie.

div.outerDivBlue span.innerSpanLarge,
//shouldn't need this second line!!!
div.outerDivBlueHover span.innerSpanLarge {
	float: left;
	background: blue;
	min-width: 200px;
}

Anyway.. hopefully you can see my point! Luckily, it turns out that some dijits like dijit.form.Button allow you to override the baseClass, thus giving you this class concatenation for free.

<div dojoType="dijit.form.Button" baseClass="tomButton">Text</div>

<!-- Now I have access to exciting classes such as:
	tomButtonHover, tomButtonActive and tomButtonFocused -->

So someone’s done the hard work for us! Oh, and if you’re wondering why I didn’t just use Pseudo Classes like :hover etc.. I laugh in your general direction…IE6 knows not of such useful ‘Pseudo classes’…Oh, and final thought… IE6 seems to prefer CSS ordering to selection specificity, brilliant…!

So Thank-you MS..you are a winner!