Dynamic Richfaces Menu, MenuItem and Action

Currently I have necessity to build dynamic menu use rich:menu and rich:menuitem. I make simple POJO model class for each menu and menuitem. Of course you can use EntityBean if necessary. Menu is just used for grouping while menuitem, children of menu which has action property that will triger some action when it is clicked.

// Menu model
public class Menu{
private String menuCaption;
private boolean enabled;
// Relation to MenuItem 
private List menuItems;

public Menu(String menuCaption, List menuItems, boolean enabled){
this.menuCaption = menuCaption;
this.menuItems = menuItems;
this.enabled = enabled;
}
// don't forget getter and setter
}



// MenuItem Model
public class MenuItem{
private String menuItemCaption;
private String menuItemAction;
private boolean enabled;  

public MenuItem(String menuItemCaption, String menuItemAction, boolean enabled){
this.menuItemCaption = menuItemCaption;
this.menuItemAction = menuItemAction;
this.enabled = enabled;
}
// don't forget getter and setter
}

Here are MenuManagedBean class. Property menuList will be refered in page and make some kind of this representation.
Menu1 Menu2
-MenuItem1 -MenuItem5
-MenuItem2 -MenuItem6
-MenuItem3
-MenuItem4

public class MenuManagedBean{
private List menuList;
public List getMenuList(){
List menuItemList1 = new ArrayList();
MenuItem menuItem1 = new MenuItem("Menu Item1", "#{someManagedBean.someAction}", true);
MenuItem menuItem2 = new MenuItem("Menu Item2", "#{someManagedBean.someAction}", true);
MenuItem menuItem3 = new MenuItem("Menu Item3", "#{someManagedBean.someAction}", true);
MenuItem menuItem4 = new MenuItem("Menu Item4", "#{someManagedBean.someAction}", true);
menuItemList1.add(menuItem1);
menuItemList1.add(menuItem2);
menuItemList1.add(menuItem3);
menuItemList1.add(menuItem4);

List menuItemList2 = new ArrayList();
MenuItem menuItem5 = new MenuItem("Menu Item5", "#{someManagedBean.someAction}", true);
MenuItem menuItem6 = new MenuItem("Menu Item6", "#{someManagedBean.someAction}", true);
menuItemList2.add(menuItem5);
menuItemList2.add(menuItem6);

Menu menu1 = new Menu("Menu1", menuItemList1, true);
Menu menu2 = new Menu("Menu2", menuItemList2, true);


menuList = new ArrayList();
menuList.add(menu1);
menuList.add(menu2);
return menuList;
}
// getter and setter
}

Next is faces page. You can use facelets or jsp page. For your notice we use jstl c:forEach tag for looping menuList in MenuManagedBean. We can not use ui:repeat or a4j:repeat here. They will not work because ui:repeat and a4j:repeat is rendered on runtime. Otherwise c:forEach is compiled before.

Eventhough the faces page will be shown, and every other value attribute appear correctly, the action attribute is not work. When we click a menuitem, rather than call appropriate string action from menuItemAction property of class MenuItem, its try to call menuItemAction method from menuItem managed bean which don’t exist.

<c:forEach var="listMenu" items="#{menuManagedBean.menuList}">
<rich:dropDownMenu disabled="#{!listMenu.enabled}">
<f:facet name="label">
<h:panelGroup>
<h:outputText value="#{listMenu.menuCaption}"/>
</h:panelGroup>
</f:facet>
<c:forEach var="menuItem" items="#{listMenu.menuItemList}">
<%-- The action attribute bellow is not work --%>
<rich:menuItem value="#{menuItem.menuItemCaption}" action="#{menuItem.menuItemAction}" disabled="#{!menuItem.enabled}"/>
</c:forEach>
</rich:dropDownMenu>
</c:forEach>

After some trial and error, I use binding aproach. I add htmlMenuItem property from class org.richfaces.component.html.HtmlMenuItem. This property will be use as binding value in revised MenuManagedBean class bellow.

import org.richfaces.component.html.HtmlMenuItem;
public class MenuItem{
private String menuItemCaption;
private String menuItemAction;
private boolean enabled;  
private HtmlMenuItem htmlMenuItem;

public MenuItem(String menuItemCaption, String menuItemAction, boolean enabled){
this.menuItemCaption = menuItemCaption;
this.menuItemAction = menuItemAction;
this.enabled = enabled;
}

// getter setter

}


public class MenuManagedBean{
private List menuList;
public List getMenuList(){
List menuItemList1 = new ArrayList();
MenuItem menuItem1 = new MenuItem("Menu Item1", "#{someManagedBean.someAction}", true);
// make binding
HtmlMenuItem htmlMenuItem = new HtmlMenuItem();
Class[] params = {};
MethodExpression actionExpression = app.getExpressionFactory().createMethodExpression(ctx.getELContext(),
                    menuItem1, String.class, params);
            htmlMenuItem.setActionExpression(actionExpression);
            htmlMenuItem.setDisabled(!menuItem1.getEnabled());
            htmlMenuItem.setValue(menuItem1.getMenuItemUrl());
            menuItem1.setHtmlMenuItem(htmlMenuItem);
            
MenuItem menuItem2 = new MenuItem("Menu Item2", "#{someManagedBean.someAction}", true);
// make binding same as above
// ...
MenuItem menuItem3 = new MenuItem("Menu Item3", "#{someManagedBean.someAction}", true);
// make binding same as above
// ...

MenuItem menuItem4 = new MenuItem("Menu Item4", "#{someManagedBean.someAction}", true);
// make binding same as above
// ...

menuItemList1.add(menuItem1);
menuItemList1.add(menuItem2);
menuItemList1.add(menuItem3);
menuItemList1.add(menuItem4);

List menuItemList2 = new ArrayList();
MenuItem menuItem5 = new MenuItem("Menu Item5", "#{someManagedBean.someAction}", true);
// make binding same as above
// ...

MenuItem menuItem6 = new MenuItem("Menu Item6", "#{someManagedBean.someAction}", true);
// make binding same as above
// ...

menuItemList2.add(menuItem5);
menuItemList2.add(menuItem6);

Menu menu1 = new Menu("Menu1", menuItemList1, true);
Menu menu2 = new Menu("Menu2", menuItemList2, true);


menuList = new ArrayList();
menuList.add(menu1);
menuList.add(menu2);
return menuList;
}
}

Last step is we use binding attribute to refer every menu and its menu item. Finally, dynamic menu item work as I expect

<c:forEach var="listMenu" items="#{menuManagedBean.menuList}">
<rich:dropDownMenu disabled="#{!listMenu.enabled}">
<f:facet name="label">
<h:panelGroup>
<h:outputText value="#{listMenu.menuCaption}"/>
</h:panelGroup>
</f:facet>
<c:forEach var="menuItem" items="#{listMenu.menuItemList}">
<%-- We make change here -->
<rich:menuItem binding="#{menuItem.htmlMenuItem}"/>
</c:forEach>
</rich:dropDownMenu>
</c:forEach>

6 respons untuk ‘Dynamic Richfaces Menu, MenuItem and Action

    • lamida berkata:

      What problem that appear? I have use above source code and it works as I expect. I use # instead $ jsp expression. Probably you have some library issue. Use latest jstl version.

  1. maLa berkata:

    masih gagal… 😦

    htmlMenuItem.setActionExpression(actionExpression);

    method setActionExpression undefined in htmlmenuitem 😦

    what should i do… ?

    • lamida berkata:

      Kemungkinan besar kamu salah import. Pastikan yang kamu import class org.richfaces.component.html.HtmlMenuItem bukan HtmlMenuItem yang lain. Kalo yang ini udah yakin kemungkinan lain adalah masalah referensi library. Kalo aku makenya Richfaces 3.3.1.

Tinggalkan komentar