# Dynamic MenuBar with dynamic popup items

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?

Dave

delete retag edit

## 25 Replies

9393 3 7 16
http://www.oxitec.de/

You can use the classes from this sample app.
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

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?

9393 3 7 16
http://www.oxitec.de/

You must debug it.
begin here:

...
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 answered 2009-12-15 09:53:50 +0800 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 answered 2009-12-15 11:04:15 +0800 btakacs 153 3 4 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 answered 2009-12-20 20:03:48 +0800 iantsai 2755 1 Where you put your label? I searched the whole article but can't find where you declare it. answered 2009-12-22 07:15:25 +0800 btakacs 153 3 4 Hi, Iantsai If you ask me, I set the label in this line: mi.setLabel(treeNode.getName()); Regards: Bence answered 2009-12-23 09:49:41 +0800 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!

drishtisv
135 2

Hi btakacs,

btakacs
153 3 4

public void render(Menuitem ti, Object node) throws Exception;
}

[hide preview]