ZK - Open Source Ajax Java FrameworkZK - Open Source Ajax Java Framework

Best practice and performance for menu tree | zul vs. java

terrytornadoTop Contributor
13 Nov 2008 19:14:30 GMT
13 Nov 2008 19:14:30 GMT

hi all,

i have a technical question about best practice and performance for creating a menu tree.


The menu tree(s) in the planned application should as follows:

1. the menu has follow structure ( + = collapsable ):

  +MainData
     -Tax
     -InvoiceConditions
     -Banks
     - ...

  +OfficeData
     -Customers
     -Orders
     -Employees
     -...

  +Statistical
     -...

  +Setup
     -Usermanagement
     -...

2. is changed by the role of the logged in user
3. should be multi-languagable

---------------------------------------------------------------------------------
A. Is it better to take all existing menu-items in the zul-file
    and by rendering zk looks for each items at the 'if=' attribut?
B. or is it better to build the menu in the underlaying java-file?
----------------------------------------------------------------------------------
C. By my first look it seems that it's a big work to create the tree-menu per coding
    as follows with about 50 items

     // create the menu trees
     Tree menuKategoryFilialstammdaten = new Tree();

     Treechildren treeChildFilialStammdaten = new Treechildren();
     Treeitem treeItem = new Treeitem();
     Treerow treeRow = new Treerow();
     Treecell treeCell = new Treecell();
     treeCell.setLabel("Filialstammdaten");
     treeRow.appendChild(treeCell);
     treeItem.appendChild(treeRow);
     treeChildFilialStammdaten.appendChild(treeItem);
     menuKategoryFilialstammdaten.appendChild(treeChildFilialStammdaten);

     ...

Are there helper classes or is this the false way for creating a tree menu (or using TreeModel? ) ??

Any experience are welcome.
Many thanks for your answers and helps.

Stephan

meherss
13 Nov 2008 22:10:53 GMT
13 Nov 2008 22:10:53 GMT

Stephan,

Actually, I am also trying to do the similar implementation using Menu (your are trying with Tree), you are little head of me :-). When user login, build menu (drill down) depending the organizational unit, roles and group the person belong to. Certainly interested to see how this plays out.

- Meher

johnfatt
14 Nov 2008 03:19:14 GMT
14 Nov 2008 03:19:14 GMT

Hi,
I am also doing a similar menu from Tree component.
I let my user to maintain(add,update,delete) module, sub-module, and function in my system and then the system will load the Menu from the database. I use SimpleTreeModel and quite a lot HaspMap and TreeMap var(TreeMap is used to maintain the menu item position set by user) to construct all the necessary SimpleTreeNode.
So i think if your system is using "fixed" menu (which mean the menu items are not likely to change), i think using a zul file to render the menu is good enough. Even in production, You can edit the zul directly if needed and no need compiling java etc. Furthermore, zul is more readable, instead of all the messy appendChild or setParent thingy in a java file.

Regards,
John

ziccardi
14 Nov 2008 09:18:46 GMT
14 Nov 2008 09:18:46 GMT

Here is my opinion.

The real answer depends on your point of view.

If your issue is performance, than a JAVA file would be surely better (zscripts are interpreted by beanshell).
Using Java, I would use ZUL to describe the view, a TreeModel for the data, a renderer to create the tree row and a composer to interact with the zul components.

If your issue is 'how much readable is your code', than ZUL is should be your choice.

About the point C:
If you want to build the tree by java code, don't build it manually. Use a model and e renderer: it will save you lot of code.

Conclusions
Usually I prefer to use the first solution: ZUL + MODEL + RENDERER + COMPOSER.
It makes the code quite readable and easy to write, making the development of the application very fast.

Regards,
Massimiliano

robinbak
18 Dec 2008 08:52:25 GMT
18 Dec 2008 08:52:25 GMT

Hello,

My personal favorite for this problem is the following:
- create your own model, for example a simple arraylist with a Java Bean's with properties like: "label", "action", "roles" etc.
- use XStream to write this model to an xml file.
- Make a helper class to generate (dynamically) a tree menu based on the model from above.

The advantages:
- Creating a new menu is now just a matter of copy/paste/edit a new (Xstream) menu xml file.
- And this menu can be role based

gr

terrytornadoTop Contributor
18 Dec 2008 12:01:50 GMT
18 Dec 2008 12:01:50 GMT

@all

many thanks for your proposals. Yes, it's what i thinking too.

I would prefer the following way:
- a empty zul-file (only the window tag)
- a model for the menu entries for filtering the user,role,menu, action
- a renderer
- a controller

At time we have on our Zkoss sample app (Zkoss+Spring+Hibernate) finish the migrating from 'fat' zul-file menu to manually java-generated menu. But the next step should to do it with a model and a renderer because the role,user,menu,action,destination should be come from the database.

I think so it's the best way to reuse this menu logic in other apps or extend by needing.

1. Does somebody have samples for a model and renderer for using as menu logic in
a menu first with two depth (Menu-Category + Menu-Entry ?
2. Our sample app should go for everyone. So we need an address to host the eclipse project.


thanks Stephan
sge(at)forsthaus(dot)de

robinbak
19 Dec 2008 09:41:36 GMT
19 Dec 2008 09:41:36 GMT

Hello here is an example how to dynamically create a tree menu based on a dedicated model:

###--- TestMenu.zul -------

<?page id="testZul" title=" New ZUL Title" cacheable="false"
language="xul/html" zscriptLanguage="Java" contentType="text/html;charset=UTF-8"?>

<zk>
<window id="w" use="com.dll.zk.comp.RoleBasedTreeMenuCtrl">
<tree id="idTree" visible="true" height="100px" width="300px"/>
</window>
</zk>

###--- menu.xml
<list>
<com.dll.zk.comp.RoleBasedMenuItem>
<label>Registreer</label>
<description>Registreer de inkomende documenten</description>
<action>REGISTER_INCOMING_DOC</action>
</com.dll.zk.comp.RoleBasedMenuItem>

<com.dll.zk.comp.RoleBasedMenuItem>
<label>Call Me</label>
<description>Bel die klant nu eens op</description>
<roles>
<string>MO_AGENT</string>
</roles>
<action>CUSTOMER_CALLME</action>
<arguments>
<string>P1</string>
</arguments>
</com.dll.zk.comp.RoleBasedMenuItem>
</list>

###--- com/dll/zk/comp/RoleBasedMenuItem.java

package com.dll.zk.comp;

import java.util.ArrayList;
import java.util.List;

public class RoleBasedMenuItem {

private String label;
private String description;
private List<String> roles;
private String action;
private List<String> arguments;
private List<RoleBasedMenuItem> children;

//---- getters & setters omiited
}


###--- com/dll/zk/comp/RoleBasedTreeMenuCtrl.java

package com.dll.zk.comp;

import java.util.List;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treechildren;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.Treerow;
import org.zkoss.zul.Window;

import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;
import com.dll.zk.helper.EventBroker;

public class RoleBasedTreeMenuCtrl extends Window {

private static final long serialVersionUID = -1636111928349775166L;

private RoleBasedMenuItem menuItems;

public static final String ATTR_MODEL_ITEM = "modelItem";

public void onCreate()
{
testMe();
}

private void testMe() {
CommandCtrl.getInstance();
String roles[] = new String[] {"MO_AGENT"};
generateMenu("/menu.xml", roles);
}

@SuppressWarnings("unchecked")
public void generateMenu(String aResourceName, String aRoles[])
{
List<RoleBasedMenuItem> menuitems = (List<RoleBasedMenuItem>)
XstreamHelper.readObject(this, aResourceName);

generateMenu(menuitems, aRoles);
}

public void generateMenu(List<RoleBasedMenuItem> aMenuItems, String aRoles[])
{
Treechildren treeChildren = getTree().getTreechildren();
if (treeChildren == null) {
treeChildren = new Treechildren();
getTree().appendChild(treeChildren);
}

for (RoleBasedMenuItem rbMenuItem : aMenuItems) {

if (isInRole(rbMenuItem, aRoles)) {
Treerow treerow = new Treerow();

Treecell treecell = makeTreeCell(rbMenuItem);
treerow.appendChild(treecell);

Treeitem treeitem = new Treeitem();
treeitem.appendChild(treerow);
treeChildren.appendChild(treeitem);
}
}
}

@SuppressWarnings("unchecked")
private Treecell makeTreeCell(RoleBasedMenuItem rbMenuItem)
{
Treecell result = new Treecell(rbMenuItem.getLabel());
result.addEventListener("onClick", new HandleMenuClick());
result.getAttributes().put(ATTR_MODEL_ITEM, rbMenuItem);
result.setTooltip(rbMenuItem.getDescription());
return result;
}

/**
* if no menuItem has no roles, then we assume it is for the whole world.
* the same is true if the user has NULL roles.
* @param aRbMenuItem
* @param allowedRoles
* @return
*/
private boolean isInRole(RoleBasedMenuItem aRbMenuItem, String allowedRoles[])
{
if (aRbMenuItem.getRoles() == null || aRbMenuItem.getRoles().size() == 0) {
return true;
} else {
if (allowedRoles == null) {
return true;
} else {
for (String allowedRole : allowedRoles) {
for (String role : aRbMenuItem.getRoles()) {
if (role != null && allowedRole != null &&
allowedRole.toUpperCase().equals(role.toUpperCase())) {

return true;
}
}
}
}
}
return false;
}

public Tree getTree()
{
return (Tree) getFellow("idTree");
}

public RoleBasedMenuItem getMenuItems() {
return menuItems;
}

public void setMenuItems(RoleBasedMenuItem menuItems) {
this.menuItems = menuItems;
}

//--- inner class
private class HandleMenuClick implements EventListener
{
public void onEvent(Event event) throws Exception
{
Treecell tc = (Treecell) event.getTarget();
RoleBasedMenuItem rbItem = (RoleBasedMenuItem) tc.getAttribute(ATTR_MODEL_ITEM);
EventBroker.getInstance().dispatchEvent(EventsConst.MENU_CLICK, rbItem);
}
}
}

###----- com/dll/zk/helper/EventBroker

package com.dll.zk.helper;

import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;

public class EventBroker extends AbstractComponent {

private static final long serialVersionUID = 1840221861723658854L;

private static final String KEY = "EventBrokerKey";

public static EventBroker getInstance()
{
EventBroker result = (EventBroker) Executions.getCurrent().getDesktop().getSession().getAttribute(KEY);
if (result == null) {
result = new EventBroker();
Executions.getCurrent().getDesktop().getSession().setAttribute(KEY, result);
}
return result;
}

public void dispatchEvent(String aEventname, Object aData)
{
Events.sendEvent(new Event(aEventname, this, aData));
}
}

terrytornadoTop Contributor
19 Dec 2008 10:43:10 GMT
19 Dec 2008 10:43:10 GMT

@robinbak

Many thanks. It looks interesting. I need a little bit time to check how it's work and do an adaption from the menu.xml to database tables

Stephan