Using dijit.Tree with the new Dojo Object Store (dojo.store)

A colleague asked me today how a dijit.Tree can be used with the new dojo.store architecture, which will supersede the legacy dojo.data architecture. Every tutorial he found on the web used ItemFileReadStore, which implements the old standard. Later that day I stumbled upon a similar question on StackOverflow and realized that a short write-up might benefit others as well.

A dijit.Tree

First things first. We need an html-file that pulls in dojo and the css-files for a theme – in this example we’ll use the claro theme. Nothing fancy here.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>Tree structure using the new Dojo Object Store</title>
 
		<link type="text/css" rel="stylesheet" media="screen" 
			href="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dijit/themes/claro/claro.css" />
 
		<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js" type="text/javascript"></script>
		<script src="js/index.js" type="text/javascript"></script>
	</head>
 
	<body class="claro">
		<h1>Tree structure using the new Dojo Object Store</h1>
		<div id="treeNode"></div>
	</body>
</html>

As we’re (always) coding unobtrusively, all JavaScript/Dojo functionality is contained within an external file – in this case js/index.js.

First, we require all the packages we need. In this example, we’ll be using the Memory Store, as it doesn’t require any server side code. We could of course have used any other dojo.store, e.g. the RESTful dojo.store.JsonRest and the example would still be valid. The dojo.data.ObjectStore will act as a wrapper – you give it an object store, and it returns, wrapped as a legacy data store.

dojo.require("dijit.Tree");
dojo.require("dojo.store.Memory");
dojo.require("dojo.data.ObjectStore");

 

We define the Object Store to a simple tree structure:

var objectStore = new dojo.store.Memory({data: [
	{id:"A", label: "Root", children:[
		{id:"1", label:"First"},
		{id:"2", label:"Second", children:[
			{id:"2a", label:"First child"},
			{id:"2b", label:"Second child"},
		]},
		{id:"3", label:"Third"},
		{id:"4", label:"Fourth"},
	]}
]});

 

dijit.Tree expects a TreeModel, which in turn expects a Data Store.

var dataStore = new dojo.data.ObjectStore({objectStore: objectStore});
var treeModel = new dijit.tree.TreeStoreModel({store: dataStore});
 
var tree = new dijit.Tree({
	model: treeModel
}, 'treeNode');
tree.startup();

 

And that’s basically it. You’d of course want to wrap the code in a dojo.addOnLoad, which would make the entire index.js look like this:

dojo.require("dijit.Tree");
dojo.require("dojo.store.Memory");
dojo.require("dojo.data.ObjectStore");
 
dojo.addOnLoad(function() {
 
	var objectStore = new dojo.store.Memory({
		data: [
			{id:"A", label: "Root", children:[
				{id:"1", label:"First"},
				{id:"2", label:"Second", children:[
					{id:"2a", label:"First child"},
					{id:"2b", label:"Second child"},
				]},
				{id:"3", label:"Third"},
				{id:"4", label:"Fourth"},
			]}
		]
	});
 
	var dataStore = new dojo.data.ObjectStore({objectStore: objectStore});
	var treeModel = new dijit.tree.TreeStoreModel({store: dataStore});
 
	var tree = new dijit.Tree({
		model: treeModel
	}, 'treeNode');
	tree.startup();
});

 

If you point your browser to the index.html file, this is what you’d see:
The browser's view of a dijit.Tree using the new dojo.store architecture
You could also take a look at this live demo.

Using a server to host the data – the JsonRest object store

So, now we’ve setup a dijit.Tree using dojo.store.Memory as the data source. While this is great for a quick mock-up Tree, you’d have to generate the JS-files dynamically if the data changes over time. And should you ever want to let your users manipulate the data through the tree – e.g. drag’n’drop or rename nodes – the Memory Store simply will not suffice.

Fortunately, changing the Memory Store to a dojo.store.JsonRest and instead using a server to host the data is quite trivial. Instead of supplying the data directly – as was the case with the Memory Store – we just supply a URL, from which we’ll get the data (in a RESTful way).

dojo.require("dijit.Tree");
dojo.require("dojo.store.JsonRest");
dojo.require("dojo.data.ObjectStore");
 
dojo.addOnLoad(function() {
	var objectStore = new dojo.store.JsonRest({target: "<URL-to-JSON-data>"});
 
	var dataStore = new dojo.data.ObjectStore({objectStore: objectStore});
	var treeModel = new dijit.tree.TreeStoreModel({store: dataStore});
 
	var tree = new dijit.Tree({
		model: treeModel
	}, 'treeNode');
	tree.startup();
});

Which URL should we be using? The simplest case, would of course be just giving it a URL to a static json-file on the server. Even though this would work quite nicely to just show the Tree, it’s hardly an improvement to the Memory Store. Like stated above – we want to generate the JSON-data dynamically. And to allow users to modify the data, we need full REST-support – i.e. not only GET a file, but also POST, PUT and DELETE support. Any server that can send JSON-encoded objects in a RESTful way would do.

Next, we’ll take a look at how to accomplish this using PHP with ZendFramework.

Closing remarks

It should be noted that this solution is ”the easy way” to do it, meaning least time to implement. Since the TreeModel expects a Data Store, we used a wrapper to morph our Object Store into a Data Store. This step naturally adds some overhead to our solution. For performance reasons, you might want to create your own TreeModel, which takes an object store directly and thus, eliminates the need for a wrapper. Or you could always wait for the Dojo Community to add one to the toolkit. If there’s an interest I’ll put together another write-up on how to create your own TreeModel that eliminates the need for a wrapper.

This entry was posted in JavaScript and tagged , , , . Bookmark the permalink.