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.

Styling dijit.form.Button, the easy way!… with baseClass

As a follow up to my previous post, I’d like to add to my final comment about using the baseClass attr on dijit.form.Button to make styling nice and easy!

In the past, when trying to use the class attr to skin a dojo button, I’ve always came across problems, because they class you add ends up about 4 layers deep into the Dijit, thus making it hard to get a handle to the outer elements of it.
To get around this, I’ve adopted a method of ‘wrapping’ a button in a span with an appropriate class as follows:

<span class="roundBlueButton">
	<div dojoType="dijit.form.Button">Text</div>
</span>

Allowing CSS as follows:

span.roundBlueButton .dijitButton {
	//now let's style it!!
}

BUT, obviously that’s not ideal now is it?!
So digging around inside some dijit button demos, (search for Test_Button.html in the dijit code base) I found an example where the class “acmeButton” is added to some buttons, not using ‘class’, but by using the mythical ‘baseClass’ attribute. It seems that this attribute overrides the class on the outermost element of a dijit.form.Button and allows you to now, finally, get a handle on that button and unleash your creative side!

<div dojoType="dijit.form.Button" baseClass="tomButton">Text</div>
.tomButton {
	//now let's style it!!
}

Even better still, this seems to remove the dijitButton class rather than adding to it, meaning that your dijit Button, is now a nice, plain black bordered, white backgrounded button…basically a blank canvas!!

This custom class you have added, now get’s used in the button’s CSS mix in functions. So in the case of my tomButton class, you’ll find that your dijit gets supplemented with tomButtonHover, tomButtonActive and tomButtonFocused under the right circumstances. Thus allowing you to style hover over effects etc.. for troublesome browsers such as IE6, IE7, IE8, probably IE9 and then IE10, 11, 12 etc.. going forward!

So there you have it! An easy way to style a dijit Button! This ‘baseClass‘ attribute should be applicable to many different types of button!
So good luck and enjoy!!

As a final comment: I’ve found that in some cases you need to add ‘!important‘ tags to your CSS classes in order to ensure that they get applied. This seems to be required when trying to apply background images etc…

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!