-
FEATURED COMPONENTS
First time here? Check out the FAQ!
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??
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();
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.
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
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?
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(); } }
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); } }
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(); }
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; } }
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); } }
<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>
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);
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
Asked: 2009-06-18 14:44:49 +0800
Seen: 4,828 times
Last updated: Feb 07 '13