0

Dynamic tree example with data fetched on demand? [closed]

asked 2009-06-18 14:44:49 +0800

davout gravatar image davout
1435 3 18

Is there an example somewhere that shows how to build/maintain a tree and its model where the tree node data is fetched on demand, i.e. when a node is opened the underlying code checks to see if child nodes exist and adds child nodes on demand. This is different to the BigArray examples, where the entire array/model is created up front.

Here's an example country/region/city tree scenario of what I'm looking to create...
* A tree is populated at start with a list of country nodes.
* When a user clicks on the expand/open widget for a given country the underlying code checks its data tier to find out what regions exist for that country
* The code then pushes the found regions into the tree model as child nodes of the selected country
* When a user clicks on the expand/open widget for a given region the underlying code checks its data tier to find out what cities exist for the selected region.
* The code then pushes the found cities into the tree model as child nodes of the selected region.

Ideas??

delete flag offensive retag edit

The question has been closed for the following reason "too localized" by sjoshi
close date 2013-02-08 05:48:41

7 Replies

Sort by ยป oldest newest

answered 2009-06-19 07:50:05 +0800

evpole gravatar image evpole
481 2

i'm a beginner ,not sure weather this will help or not:
you may prepare all the country,region and city nodes in the tree model at first.when user click a country node, the region node for this country will be filled automaticlly.
you may wish to test when the node was filled,using : tree.getItems();

link publish delete flag offensive edit

answered 2009-06-19 08:21:28 +0800

davout gravatar image davout
1435 3 18

thanks for the comment, but your suggestion is not about dynamic model building and assumes a small dataset.

Elsewhere on this site a contributor posted a superb article how to make a grid data model dynamic/data on demand, I'm hoping that somebody in ZK can step up and make the same for the Tree.

Given the popularity of tree GUI in most app's this is hardly an unusual request.

link publish delete flag offensive edit

answered 2009-06-22 07:27:48 +0800

Rico gravatar image Rico
175 1 1 2

Hi,

I am working on an application that does just this. The way I/We have solved it is to use a set of classes:

- A tree model class (extending the AbstractTreeModel).
- A tree node class that contains the data for a tree node.

The model class more or less calls the supplied node's methods for isLeaf(Object), getChild(Object, int) and getChildCount(Object) since the node class have methods corresponding to these methods in the tree model:

TreeNode getChildIndex(TreeNode child);
int getChildCount();
boolean isLeaf();

and also:

TreeNode getChild(int index), void setParent(), TreeNode getParent(), void addChild(TreeNode node)

and maybe most important: abstract void readChildren()

Each node also have an ArrayList<TreeNode> of its children and a data bean.

Whenever information about a node's children are needed, the array is looked upon. If the array is null, readChildren() is called. Since this method is declared abstract in the base class, each node in the tree needs to implement a way to retrieve its children.

This means that whenever a node is opened to reveal its children, either the existing array is used, or the children are fetched using a data base lookup. Each node can have it's own implementation of this.

Of course, this is a brief overview of the idea behind our dynamic tree implementation, there are a lot of other things going on (a TreeItemRenderer for instance), but hopefully it gives You an idea on how to proceed (or how NOT to proceed :) )

Regards,
Rico

link publish delete flag offensive edit

answered 2009-06-23 01:05:22 +0800

gmitev gravatar image gmitev
190 3

rico

I also need to do something like this, can you provide some sample code, mainly, initial tree population, on click of node and dynamically populate tree.

The demo that I have seen seems to use ArrayList for the parent nodes, this seems very strange to me?

link publish delete flag offensive edit

answered 2009-06-23 06:35:26 +0800

Rico gravatar image Rico
175 1 1 2

updated 2009-06-23 11:15:40 +0800

Since my code is embedded in bigger projects, I cannot cut'n'paste the complete code, so instead I have done a stripped down version. The code is of pseudo-type though, and I have not actually run it, so chances are that it will not even compile, but I hope it gives You an idea of how it could be implemented (there are probably billions of better ways to implement this, but it works for me!)

Anyway, here it goes:

First the MyTreeModel class that is used as model for the tree:

import org.zkoss.zul.AbstractTreeModel;

public class MyTreeModel extends AbstractTreeModel
{
	MyTreeNode root;

	public MyTreeModel(MyTreeNode root)
	{
		super(root);

		this.root = root;
	}

	//@Override
	public Object getChild(Object arg0, int arg1)
	{
		return ((MyTreeNode)arg0).getChild(arg1);
	}

	//@Override
	public int getChildCount(Object arg0)
	{
		return ((MyTreeNode)arg0).getChildCount();
	}

	//@Override
	public boolean isLeaf(Object arg0)
	{
		if(arg0 == null)
			return true;

		return ((MyTreeNode)arg0).isLeaf();
	}
}


As You can see, the model class does not do much on its own, but rather relays all the action to the given MyTreeNode, which could look something like:
import java.util.ArrayList;

public abstract class MyTreeNode
{
	ArrayList<MyTreeNode> children = null;

	public abstract void readChildren();

	public ArrayList<MyTreeNode> getChildren()
	{
		return children;
	}

	public MyTreeNode getChild(int arg1)
	{
		MyTreeNode child = null;

		if (children == null)
			readChildren();

		if (children != null && (arg1 > -1 && arg1 < children.size()))
			child = children.get(arg1);

		return child;
	}

	public int getChildCount()
	{
		if (children == null)
			readChildren();

		if (children != null)
			return children.size();

		return 0;
	}

	public boolean isLeaf()
	{
		return (getChildCount() == 0);
	}
}


This class contains an array list to keep track of all children, and the "magic" is done by calling readChildren() whenever the framework need the children for the first time. Since readChildren() is declared abstract, we must create one or more classes for the different kinds of nodes in the tree.

It could be one class handling all types of nodes regardless of the level, or as I have done it, make a sub class for each type of node that is in the tree.

We start with a root node:
import java.util.ArrayList;

public class RootNode extends MyTreeNode
{
 @Override
 public void readChildren()
 {
   // Connect to database and read data
   // children will be an array list of CountryNode objects
   children = DBAccess.readCountries();
 }


The root node is the base of the tree and not visible, hence it reads all the children for the first actual level of the tree, the countries (as was asked for in the original question). It implements readChildren() and by some means reads an array list from a data base (which I left out of this totally). The returned value from DBAccess.readCountries() should be an ArrayList<CountryNode> object.

And the CountryNode class might look something like this:
import java.util.ArrayList;

public class CountryNode extends MyTreeNode
{
 // In the real application I don't have any "data base" data in the nodes,
 // but rather each node have a bean that contains this data.
 Long country_id = null;
 String country = "";

 @Override
 public void readChildren()
 {
  // Connect to database and read data, children will be an array list of CityNode objects
  // given this country node's country_id
  children = DBConnection.readCities(country_id);
 }

 public void setCountry_Id(Long id)
 {
  country_id = id;
 }

 public Long getCountry_Id()
 {
  return country_id;
 }

 public void setCountry(String country)
 {
  this.country = country;
 }

 public String getCountry()
 {
  return country;
 }
}


Here we have some actual data, country_id and country which are read from the data base in the RootNode. The country id is then used when the cities should be read (i.e. when the TreeModel calls for information about the children of a country).

Then there should be a CityNode and what ever other nodes needed. And as I mentioned in my previous post, there are loads of helper methods that could be added to the MyTreeNode class, getParent(), setParent(MyTreeNode), addChild(MyTreeNode), and so on.

There is probably also the need for a TreeitemRenderer class, that can be as simple as:
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.TreeitemRenderer;
import org.zkoss.zul.Treerow;;

public class MyTreeitemRenderer implements TreeitemRenderer
{
	// @Override
	public void render(Treeitem item, Object data) throws Exception
	{
		item.setValue(data);

		// Construct treecells
		Treecell tcNamn = new Treecell(data.toString());
		Treerow tr = null;

		if (item.getTreerow() == null)
		{
			tr = new Treerow();
			tr.setParent(item);
		}
		else
		{
			tr = item.getTreerow();
			tr.getChildren().clear();
		}
		// Attach treecells to treerow
		tcNamn.setParent(tr);

		String icon = null;

		if (data instanceof CountryNode)
			icon = "country_16x16.png";
		if (data instanceof CityNode)
			icon = "city_16x16.png";
		if (data instanceof StreetNode)
			icon = "street_16x16.png";

		if (icon != null)
			item.setImage(icon);

		item.setOpen(false);
	}
}


(here I have added an icon for each item depending on which type of node it actually is, just to show how one can use the fact that each node is a different class. I also added CityNode and StreeNode that I didn't define above).

Then there must be a way to kick start everything, and I do this in a zul file:
<zk>
 <zscript>
  RootNode root = new RootNode();
  root.readChildren();

  MyTreeModel mtm = new MyTreeModel(root);
  MyTreeitemRenderer mtr = new MyTreeitemRenderer();
 </zscript>
 <tree id="my_tree" model="${mtm}" treeitemRenderer="${mtr}" height="800px" />
</zk>


Note that I "force" the root to read its children to make sure they are in place when the tree(model) is created.

Hope this helps and at least gives You an idea of how it can be done, and please feel free to ask any questions that might pop up.

Again, the code is not tested in any way, just cut'n'pasted from my projects with lots of code removed for clarity and simplicity.

Regards,
Rico

link publish delete flag offensive edit

answered 2009-06-24 05:28:22 +0800

gmitev gravatar image gmitev
190 3

Thanks Rico,

I implemented in a similar yet slightly different way, my solution is perhaps not as elegant, yet it is simple and neat.

The main diffference is when I click on a node, I do:-

Treeitem item = tree.getSelectedItem();

MyTreeNode node = (MyTreeNode ) item.getValue();
Object obj = node.getData();

List children = this.getProductNodes();

int index = item.indexOf();

//create a new node with the children and update
//the array list of the model
//THIS IS THE NOT SO NICE BIT
node = new MyTreeNode(data, children);

//this si the list that is added to my model
//contains all parent nodes
allItems.set(index, node);

//update the node
productTree.renderItem(item);

link publish delete flag offensive edit

answered 2009-06-24 06:28:49 +0800

Rico gravatar image Rico
175 1 1 2

You're welcome...

By using the tree model's built-in mechanisms for handling the population of the tree makes it possible to use the onSelect handler for other purposes. I.e. selecting a node can trigger a different action then actually opening the node to revela its children (if any).

Also I think (but I am not sure of it) that if You programatically opens a node, say from Java code, it will not trigger the onSelect event, but it will make the tree model do its thing anyway.

Trees are a useful but sometimes cumbersome tool to use. While not overly complex, they still takes some thinking to get right, and what is right on one occasion may not be the best way the next time.

/Rico

link publish delete flag offensive edit

Question tools

Follow

RSS

Stats

Asked: 2009-06-18 14:44:49 +0800

Seen: 4,828 times

Last updated: Feb 07 '13

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More