0

Dynamic MenuBar with dynamic popup items

asked 2009-12-07 12:43:38 +0800

dhamu gravatar image dhamu
165 1 6

I want to create a menubar with sub menus and sub-sub menus for my application. However, the actual list of items appearing in my menus must be dynamic. I have the logic to retrieve the menu configuration at run-time from the database. However, I need to figure out an elegant way to populate the menubar with the various labels, and menuitems.

Obviously, this cannot be done with vanilla ZK. Any suggestions? Does it make sense to use the ZK API and write the logic in Java or is there a way to manipulate the content of a menubar and the various labels, menupopups and menuitems through scripting?

Your assistance is greatly appreciated.

Dave

delete flag offensive retag edit

25 Replies

Sort by » oldest newest

answered 2009-12-07 12:54:58 +0800

terrytornado gravatar image terrytornado flag of Germany
9393 3 7 16
http://www.oxitec.de/

updated 2009-12-07 12:55:33 +0800

You can use the classes from this sample app.
You'll find it in the packes de.forsthaus.common.menu..... TreeMenu + PulldownMenu
In that case the menu entries comes fom a xml-file. You must search where you can replace that logic to read from your database .

best
Stephan

link publish delete flag offensive edit

answered 2009-12-07 13:12:41 +0800

dhamu gravatar image dhamu
165 1 6

Wow! I'm looking all over the source code for the specific code that you are referring to and I'm wondering if your link is pointing to the correct application. I don't even see reference to a menubar. Can you offer just a little more direction?

link publish delete flag offensive edit

answered 2009-12-07 13:36:13 +0800

terrytornado gravatar image terrytornado flag of Germany
9393 3 7 16
http://www.oxitec.de/

You must debug it.
begin here:

de.forsthaus.common.Menu.MainMenuCtrl.java
...
public void onClick$btnMainMenuChange(Event event) throws Exception {




As in the thread:
1. Now you can change the navigation between a TreeMenu or PullDown Menu.
All is working around a xml menu file that describes the menuitems and the depth of the menu.
Out from from this file there are java objects created that describes only a meta menu structur.
With helper classes they create the menu in case of a tree menu or a menuBar menu.
Done by Björn. Documentation of this classes takes a while.

Sorry


best
Stephan

link publish delete flag offensive edit

answered 2009-12-15 09:53:50 +0800

btakacs gravatar image btakacs
153 3 4

Hi I have a solution for this, and it is working for me.

We need an interface for our data to implement it:

public interface TreeNode {

	public Long getId();
	public String getName();
	public boolean isLeaf();
	public boolean isRoot();
	public int getChildCount();
	public TreeNode getChildAt(int childIndex);
	
}

And a TreeModel:

public class MenuTreeModel extends AbstractTreeModel {
		
		public MenuTreeModel(TreeNode root) {
			super(root);
		}
		
		public Object getChild(Object parent, int index) {
			TreeNode node = (TreeNode)parent;
			return node.getChildAt(index);
		}
		
		public int getChildCount(Object parent) {
			TreeNode node = (TreeNode)parent;
			return node.getChildCount();
		}
		
		public boolean isLeaf(Object node) {
			if(node instanceof TreeNode){
				TreeNode node_ = (TreeNode)node;
				return node_.isLeaf();
			}else{
				return true;
			}
		}

	}

..and an extended Menubar component to do the job:

public class DynamicMenu extends Menubar{

	private TreeModel _model;
	private MenuitemRenderer _renderer;
	
	public DynamicMenu(){
		super();
	}
	
	public DynamicMenu(String orient) {
		super(orient);
	}
	
	public void setModel(Object model) {
		if(!(model instanceof TreeModel)){
			if(model instanceof MenuItem){
				model=new MenuTreeModel((MenuItem)model);
			}
		}
		if (model != null && model instanceof TreeModel) {
			TreeModel tModel=(TreeModel)model;
			if (_model != tModel) {
				_model = tModel;
			}
			syncModel();
		} else if (_model != null) {
			_model = null;
		}
	}
	
	public TreeModel getModel(){
		return _model;
	}
	
	private void syncModel() {
		renderMenu();
	}
	
	private void renderMenu(){
		if(_model==null) return;
		
		Object node = _model.getRoot();
		if(_renderer==null)_renderer=_defRend;
		try {
			renderChildren(_defRend, this, node);
		} catch (Throwable ex) {
			
		} finally {
			
		}
	}
	
	private void renderChildren(MenuitemRenderer renderer, Component parent,
	Object node) throws Throwable {
		for(int i = 0; i < _model.getChildCount(node); i++) {
			Object childNode = _model.getChild(node, i);
			if(_model.isLeaf(childNode)){
				Menuitem mi= new Menuitem();
				mi.setParent(parent);
				renderer.render(mi, childNode);
			} else {
				Menu menu=new Menu();
				menu.setParent(parent);
				renderer.render(menu, childNode);
				Menupopup mp=new Menupopup();
				mp.setParent(menu);
				renderChildren(renderer, mp, childNode);
			}
		}
	}
	
	private static final MenuitemRenderer _defRend = new MenuitemRenderer() {
		public void render(Menuitem mi, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				mi.setLabel(treeNode.getName());
				mi.setId(treeNode.getId().toString());
				mi.setAction("onclick: alert(this.id)");
			} 
		}
		
		public void render(Menu menu, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				menu.setLabel(treeNode.getName());
			} 
		}
	};

}

some configuration in the lang-addons.xml:

<!-- Dynamic menu component -->
	<component>
		<component-name>dynamicmenu</component-name>
		<component-class> my.zkext.menu.DynamicMenu</component-class>
		<extends>menubar</extends>
		<property>
			<property-name>model</property-name>
			<property-value></property-value>
		</property>
	</component>

and we can use it on the page, we only need to bind the root element of our tree to the new component:

<dynamicmenu id="mainMenu" autodrop="true" model="${menuDao.root}" />

It is working, but I would need more interaction with the page.
I would like to add the functionality for this component to change attributes and call actions on the page. Now it is called "mainWin", and i would like to change for example a title there <title id="mytitle"> how can I bind a Menuitem of the

I mean something like this:

<dynamicmenu id="mainMenu" forEach="${menuDao.elements}" value="${each}" >
		<menuitem id="${each.id}" onClick="content.value=this.id"/>
	</dynamicmenu>

or from program code trigger an event from menuitem:

public void render(Menuitem mi, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				mi.setLabel(treeNode.getName());
				mi.setId(treeNode.getId().toString());
				mi.setAction("onclick: ????");
			} 
		}

and listen to it from the page Controller?

Any tips are welcome.

Best regards:
Bence

link publish delete flag offensive edit

answered 2009-12-15 11:04:15 +0800

btakacs gravatar image btakacs
153 3 4

updated 2009-12-15 11:30:30 +0800

Hi

I modified DynamicMenu this way:

...
private Object _selectedItem;
...
	private final MenuitemRenderer _defRend = new MenuitemRenderer() {
		public void render(Menuitem mi, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				mi.setLabel(treeNode.getName());
				mi.setId(treeNode.getId().toString());
				mi.addEventListener("onClick", new EventListener() {
					public void onEvent(Event event) throws Exception {
						setSelecteditem(event.getTarget().getId());
					}
				});
			} 
		}
		
		public void setSelecteditem(Object selecteditem){
			setSelectedItem(selecteditem);
		}
		
		public void render(Menu menu, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				menu.setLabel(treeNode.getName());
			} 
		}
	};
	
	public Object getSelectedItem() {
		return _selectedItem;
	}

	public void setSelectedItem(Object selectedItem) {
		_selectedItem = selectedItem;
	}

...and this way the component updates the _selectedItem value with the clicked one.
But if I would like to bind it to the page like this:

<label id="label" value="${mainMenu.selectedItem}" />

It won't show the value...

I use only the zk-spring variableresolver on the page.

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>

Do I need something else too? Maybe fire an Event in setSelectedItem and make the page catch it? If yes, how?
Or is there a way to extend my component and use it this way:
<dynamicmenu id="mainMenu" autodrop="true" model="${cmsService.menuDao.root}" onChange="label.value=this.selectedItem"/>

Thanks:
Bence

link publish delete flag offensive edit

answered 2009-12-20 20:03:48 +0800

iantsai gravatar image iantsai
2755 1

Where you put your label?

I searched the whole article but can't find where you declare it.

link publish delete flag offensive edit

answered 2009-12-22 07:15:25 +0800

btakacs gravatar image btakacs
153 3 4

Hi, Iantsai

If you ask me, I set the label in this line:
mi.setLabel(treeNode.getName());

Regards:
Bence

link publish delete flag offensive edit

answered 2009-12-23 09:49:41 +0800

btakacs gravatar image btakacs
153 3 4

Hi

This is the latest working version of dynamic menu. You can check the required interfaces, and models above:

public class DynamicMenu extends Menubar {

	private TreeModel _model;
	private MenuitemRenderer _renderer;
	private Object _selectedItem;
	
	public DynamicMenu(){
		super();
	}
	
	public DynamicMenu(String orient) {
		super(orient);
	}
	
	public void setModel(Object model) {
		if(!(model instanceof TreeModel)){
			if(model instanceof MenuItem){
				model=new MenuTreeModel((MenuItem)model);
			}
		}
		if (model != null && model instanceof TreeModel) {
			TreeModel tModel=(TreeModel)model;
			if (_model != tModel) {
				_model = tModel;
			}
			syncModel();
		} else if (_model != null) {
			_model = null;
		}
	}
	
	public TreeModel getModel(){
		return _model;
	}
	
	private void syncModel() {
		renderMenu();
	}
	
	private void renderMenu(){
		if(_model==null) return;
		
		Object node = _model.getRoot();
		if(_renderer==null)_renderer=_defRend;
		try {
			renderChildren(_defRend, this, node);
		} catch (Throwable ex) {
			
		} finally {
			
		}
	}
	
	/*
	 * Renders the direct children for the specifed parent
	 */
	private void renderChildren(MenuitemRenderer renderer, Component parent,
	Object node) throws Throwable {
		for(int i = 0; i < _model.getChildCount(node); i++) {
			Object childNode = _model.getChild(node, i);
			if(_model.isLeaf(childNode)){
				Menuitem mi= new Menuitem();
				mi.setParent(parent);
				renderer.render(mi, childNode);
			} else {
				Menu menu=new Menu();
				menu.setParent(parent);
				renderer.render(menu, childNode);
				Menupopup mp=new Menupopup();
				mp.setParent(menu);
				renderChildren(renderer, mp, childNode);
			}
		}
	}
	
	private final MenuitemRenderer _defRend = new MenuitemRenderer() {
		public void render(Menuitem mi, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				mi.setLabel(treeNode.getName());
				mi.setId(treeNode.getId().toString());
				mi.addEventListener(Events.ON_CLICK, new EventListener() {
					public void onEvent(Event event) throws Exception {
						DynamicMenu.this.setSelectedItem(event.getTarget().getId());
					}
				});
			} 
		}
		
		public void render(Menu menu, Object node){
			if(node instanceof TreeNode){
				TreeNode treeNode=(TreeNode)node;
				menu.setLabel(treeNode.getName());
			} 
		}
	};
	
	public Object getSelectedItem() {
		return _selectedItem;
	}

	public void setSelectedItem(Object selectedItem) {
		_selectedItem = selectedItem;
		Events.postEvent(new Event(Events.ON_CLICK, this, selectedItem));
	}

}

usage:

<dbmenu id="mainMenu" autodrop="true" model="${menuDao.root}" onClick="label1.value=self.selectedItem;"/> 

<label id="label1" value="${menuDao.default}" />

You will need a getRoot() and a getDefault() method in your DAO. Root element will not be rendered (this is only the entry point of the tree), so this is why you will need to define an different Default element.

Merry Christmas!

link publish delete flag offensive edit

answered 2010-06-24 07:16:46 +0800

drishtisv gravatar image drishtisv
135 2

Hi btakacs,

Where to get MenuitemRenderer Class?

link publish delete flag offensive edit

answered 2010-06-24 08:42:01 +0800

btakacs gravatar image btakacs
153 3 4

MenuitemRenderer is just an interface:

public interface MenuItemRenderer {
public void render(Menuitem ti, Object node) throws Exception;
public void render(Menu menu, Object node) throws Exception;
}

link publish delete flag offensive edit
Your reply
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2009-12-07 12:43:38 +0800

Seen: 4,133 times

Last updated: Sep 29 '10

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