Lazy load child nodes to your Dojo tree using JsonRestStore

Efficiently fetch and display large data sets

Learn to use Dojo and JsonRestStore to create a Dojo tree that uses lazy loading for child nodes. Delete, add, and update nodes by manipulating the data store with methods defined on JsonRestStore, and harness the power of lazy loading functions on Dojo trees to efficiently fetch and display large data sets.

Share:

Ina Arya, System Software Engineer, IBM

Photo of Ina AryaIna is a system software engineer with the Software Development Workbench Team at the IBM India Software Lab. She has more than five years of experience in application development, including Web applications using Java technology.



21 December 2010

Also available in Chinese Japanese

Introduction

A tree, which is used to navigate hierarchal data in parent-child relationships, can help you easily understand a long list of data and relationships between them. Dojo provides support for creating the tree, and trees in Dojo follow the Model-View-Controller (MVC) design pattern. In digit trees, the roles are played by the following components:

Controller (dojox.data store)
The store responsible for storing all the data in the non-relational database form.
Model (dijit.tree ForestStoreModel/TreeStoreModel)
Formats the data of the data store in a hierarchal order according to the type of model the user has chosen.
View (dijit.Tree)
Tree that gives a view of the data created by the model object.

With MVC in Dojo trees, tree creation starts from controller, goes to model, then to view, as shown in Figure 1.

Figure 1. Dojo tree following MVC design pattern
Pyramid showing the controller layer on the bottom, then the model layer, and then the view layer at the top.

You can create a tree in Dojo using various Dojo data stores. In this article, learn to create a lazily loaded Dojo tree using the REST API-based Dojo store JsonRestStore.

Lazy loading

Lazy loading is a technique used to fetch data from the user database only when it's required. Only those nodes that are expanded by the user will be fetched from the server, which can result in better performance of the tree. Lazy loading of a tree can result in either a cheap or expensive operation, depending on the amount of data a tree will hold.

Applying lazy loading on a small amount of data results in multiple requests to the server, and the slow response might lead to user frustration. However, in a production environment with large data sets, lazy loading always provides advantages.

Before digging deep into the Dojo tree creation process, the next section covers a few essential concepts for creating a lazy loaded tree.


Dojo tree generation

There are several important building blocks of a Dojo tree:

  • JsonRestStore referencing capability
  • deferItemLoadingUntilExpand
  • JSON data

JsonRestStore referencing capability

JSON referencing is the property provided by JsonRestStore by which you can get the reference to an object from another object (instead of including the actual object). Referencing one object from another is provided by the $ref property of the JsonRestStore.

The parent-child relationship is resolved at run time using the value of the $ref property in the parent that is the id value of the child node. The JSON object for a parent node might look like the following example.

{ 
      "id": "node1",
      "name": "Subjects",
        $ref: "child1"
}

The child JSON object will look similar to the following example.

{ 
      "id": "child1",
      "name": "Math",
        $ref: "author1"
}

In the first code example, the parent node has a $ref property value of "child1", which means it's referring to the id value of the child node. This value will be resolved at run time after the parent node is expanded (shown later in this article).

In the second code example, the id"child1" should exactly match the $ref value of the parent. You can have this hierarchy go on, similar to $ref:"author1" in the second example.

deferItemLoadingUntilExpand property

The Dojo Toolkit 1.4 introduced a new property, deferItemLoadingUntilExpand, for the Dojo tree model. Earlier versions of the Dojo Toolkit lacked this property, so when any of the child nodes were expanded all the tree nodes were fetched and loaded into the Dojo datastore. With the deferItemLoadingUntilExpand attribute, however, if deferItemLoadingUntilExpand is set to true, it will only fetch the children of the requested node and will get updated in the datastore. This helps increase the efficiency of lazy loading a tree.

JSON data for Dojo tree

JsonRestStore has a predefined schema for data in the JSON format. Before you start the tree generation, a basic understanding of JSON is strongly recommended. This article uses JSONObject, JSONArray, and JsonString for the trees so the example gets the data in the right format. The article's example will get the JSON data from the Java™ class, and will need JSON data as a return string that will be handled by the JsonRestStore.


Create a lazy loaded Dojo tree

In this section, you create a lazy loaded Dojo tree using JsonRestStore. The example creates a tree for a fictitious bookstore. The tree will have information about the books and authors, and there's a hierarchy between the books and authors. You'll create this hierarchy using a Dojo tree with lazy loading to make the application more efficient.

The Dojo tree follows the MVC model, and the model comes in two flavors: TreeStoreModel and ForestStoreModel. The example uses ForestStoreModel to construct a tree. First, though, you need to declare and define the JsonRestStore for the application. You can create a JsonRestStore by using a REST service, or by directly mapping a URL to the target parameter.

The example declares a service that places a Dojo-based Asynchronous JavaScript and XML (Ajax) call to a Java class that's responsible for getting data from the database and returning the string in the JSON format. The data, after retrieval from the Java class, gets updated to the service parameter.

Start with the generation of the client-side code, as shown in Listing 1, with the JsonRestStoreTree.jsp code.

Listing 1. Create REST service and JsonRestStore
var booksService = function(query, queryOptions) 
{	

return dojo.xhrGet({url:" 
/BookStoreTree/com/tree/actions/JsonRestStoreTreeAction.action", 
handleAs:'json',content:{query:query, queryOptions:queryOptions}});

};

var booksDataStore = new 
dojox.data.JsonRestStore({target:'booksData',service: booksService,
labelAttribute:"name"});

Or, you can create a store directly by specifying the target for the store, as shown in Listing 2.

Listing 2. Create Store using target parameter
var booksDataStore = new dojox.data.JsonRestStore({target: 
"/BookStoreTree/com/tree/actions/JsonRestStoreTreeAction.action ",
labelAttribute:"name"});

Now that the store is created, you need to create a model using ForestStoreModel. The example creates the model programmatically, as in Listing 3. The code sets the value of deferItemLoadingUntilExpand to true.

Listing 3. Create tree model
var treeModel = new dijit.tree.ForestStoreModel({
                     store: booksDataStore,
                     deferItemLoadingUntilExpand: true,
                     rootLabel: "Subjects",
                     childrenAttrs: ["children"]
       	     });

The store and model are ready, so go ahead and create the Dojo tree as shown in Listing 4.

Listing 4. Create Dojo tree
<div dojoType="dijit.Tree" model="treeModel" persist="false" id="booksDataTree">

As you did with the client-side code for tree generation, start writing Java code for getting the initial data for the books in the JSON format.

The code in Listing 5 returns the JSON format of data. The data is subsequently stored into the booksDataStore.

Listing 5. Java code to get data for initial tree
public String getSubjectList() throws IOException 
	{
		//Code to query subjects list from DB
		//call DB-side function to get the books object
		Map<String,String> booksNameMap = new
                               HashMap<String, String>();
		//ex. Map booksNameMap =
                server.GetListOfSubjects();
		//above code is specific to application
		JSONObject subObj = new JSONObject();
		JSONArray subArray = new JSONArray();
		
		for(int i=0; i< booksNameMap.size(); i ++)
		{
                      String subjectName = booksNameMap.get(i);

                      subObj.put("id", subjectName +"Id");
                      subObj.put("name", subjectName);
                      subObj.put("$ref","author"+ i);
                      subObj.put("children", true);
		}

		subArray.add(subObj);	
		jsonString = subArray.serialize(true);
		setJsonString(jsonString);
		return jsonString;
	}

The code in Listing 5 stores data in the JSONArray, which is necessary for loading the initial data of the tree. The tree that is created is shown in Figure 2.

Figure 2. Initial Dojo tree
Directory structure with subjects as main directory then subdirectories of algorithms, c programming, data structures, java programming.

Now that the tree is created, you can expand a node that fetches data for the requested node. You'll see how referencing property plays an important role when you expand any node.

In Listing 6, when the parent node with the id"subjectNameId" is expanded, a call to the getListOfAuthors() method is made. Because the $ref property of "subjectNameId " is set to "author1", the node data of author1 is returned and is automatically updated as the child node to the tree.

Listing 6. Getting data for child node
public String getListOfAuthors() throws IOException
	{
		//Code to query authors list from DB
		//call DB-side function to instantiate 				
                authorsNameMap object
		Map<String,String> authorsNameMap = new
                               HashMap<String, String>();
		//ex. Map authorsNameMap = 						
                server.GetListOfAuthors();
		//above code is specific to application
		JSONObject authObj = new JSONObject();
		
		for(int i=0; i< authorsNameMap.size(); i ++)
		{
                      authObj.put("id", "author"+i);
                      authObj.put("name",
                                authorsNameMap.get(i));
		}
	
		jsonString = authObj.serialize(true);
		setJsonString(jsonString);
		return jsonString;
	}

Irrespective of the parent data that was in the JSONArray format, the example is sending the child node's data in the JSONObject format. This is essential, as the children data is captured in the JSONObject format.

The tree that is created is shown in Figure 3. When the C Programming node is expanded, a request is being sent to the server and it is fetching data for this node only.

Figure 3. Lazy loaded Dojo Tree
Directory structure with subjects as main directory then subdirectories of algorithms, c programming (with a timer beside it), data structures, java programming.

Delete a node from the tree

When deleting, adding, or updating a node you don't update the tree view. Instead, you need to update the tree model that is connected to the data store. Essentially, you need to update the data store if you want to modify the tree.

In the bookstore example, the store data has information on various books, each identified uniquely by the "id" value of the books. You'll fetch the node using the "id" value, and will delete that from the store. Listing 7 shows the code snippet that's handling the deletion of a node from the tree.

Listing 7. Deleting a node from the Dojo tree
function deleteNodeFromTree()
	{
		//delete from the bookstore//
		booksDataStore.fetchItemByIdentity({
				identity: 'AlgorithmsId',
				onItem: dojo.hitch(booksDataStore,
                                          function(item) {
				booksDataStore.deleteItem(item);
				booksDataStore.save({
                                     onComplete: function() {
                                          alert("Save Complete");
								}
							});
						})
					});
}

The code in Listing 7 fetches the node from the store by the identity of the node ('AlgorithmsId' in this case). After you get the required node, the deleteItem() method of the JsonRestStore is called, which deletes the 'AlgorithmsId' data from the data store. After deleting the node, the Dojo tree looks like Figure 4.

Figure 4. Algorithms node is deleted
Directory structure with subjects as main directory then subdirectories of c programming, data structures, java programming.

Add a node to the tree

JsonRestStore has the newItem(item, {parent : parentItem, attribute : children}) function for adding new items to the store, and you can use it to add a new node to the tree. The method is called similarly to the way the example called deleteItem() for deleting the node. In the bookstore, add "A.K.Das" as a child of the Data Structures node.

Listing 8. Add node to Dojo tree
function addNodeToTree()
{
	//add data to the bookstore//
	booksDataStore.fetchItemByIdentity({
				identity: 'AlgorithmsId',
				onItem: dojo.hitch(booksDataStore,
                                                function(item) {
				booksDataStore.newItem(
                                          name: 'A.K.Das',
                                          ref: $'AlgorithmsId',
                                               item);
				booksDataStore.save({
				onComplete: function() {
                                          alert("Add node
                                  Complete");
								}
								});
							})
						});
}

The code in Listing 8 fetches the item where you want to add the new child. Or, in other words, the new node becomes a child node of the node you're fetching from the store. The parent node is 'AlgorithmsId', which is the Subject, and the example is adding 'A.K.Das' as the author. Figure 5 shows the tree after the addition of the new node.

Figure 5. Child node is added to Algorithms node
Directory structure with subjects as main directory then subdirectories of algorithms, c programming, data structures, java programming, with A.K. Das showing under algorithms.

Update a node of the tree

To update a node of the tree you need to update the data existing in the data store. Again, the example calls the fetchItemByIdentity method of data store and uses the setValue(item, attribute, value) method of the JsonRestStore to update the data of the target node.

Listing 9 shows the code, where the setValue(item, attribute, value) method is called on the booksDataStore, and the value of the 'name' property is changed for the chosen node.

Listing 9. Update node of the Dojo tree
function updateNodeToTree()
{
	//update data of the bookstore//
	booksDataStore.fetchItemByIdentity({
				identity: 'AlgorithmsId',
				onItem: dojo.hitch(booksDataStore,
                                                function(item) {
				booksDataStore.setValue(item, 'name',
                                                    'Discrete');
				booksDataStore.save({
				onComplete: function() {
                                          alert("Update Complete");
								}
								});
							})

The setValue(item, attribute, value) method of JsonDataStore updates the existing node property. The code in Listing 9 updates the name of the 'AlgorithmsId' node from Algorithms to Discrete. Figure 6 shows the tree after the update.

Figure 6. Node Algorithms updated to Discrete
Directory structure with subjects as main directory then subdirectories of c programming, data structures, discrete, and java programming.

Conclusion

Together, Dojo- and REST-based JsonRestStore provide excellent support for lazily loading trees that allow large data sets to be fetched and displayed efficiently. Updating, adding, and deleting Dojo trees can be easy when you use the methods provided by JsonRestStore. Dojo and JsonRestStore let you harness the lazy loading functions on Dojo trees. You can create advanced, rich text-based UIs by perfectly combining the Dojo Tree widget with a REST-based data store.

Resources

Learn

  • Read more about Dojo dijit.Tree to learn how trees help sort out long, hierarchical lists.
  • Learn more about the features and usage of JsonRestStore.
  • Explore the Dojo API for JsonRestStore. JsonRestStore will cause all saved modifications to be sent to the server using Rest commands (PUT, POST, or DELETE).
  • "Efficient Lazy Loading of a Tree" provides an example of building a lazy loaded tree with JsonRestStore.
  • Learn more about JSON, the lightweight data-interchange format that's easy for humans to read and write.

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=599374
ArticleTitle=Lazy load child nodes to your Dojo tree using JsonRestStore
publish-date=12212010