Primefaces - 如何动态地将ContextMenu添加到MenuItem

时间:2015-04-03 07:16:44

标签: primefaces menu contextmenu menuitem

我想以编程方式使用MenuItems创建p:menu(它可以工作),每个MenuItem应该有p:contextMenu(它不起作用)。

ManagedBean:

@ManagedBean(name="leftMenuView")
@SessionScoped
public class LeftMenuView {

    private MenuModel model;

    @PostConstruct
    public void init() {
        model = new DefaultMenuModel();   
        DefaultMenuItem item = new DefaultMenuItem("Redirect");
        item.setId("redirectMenuItem");
        model.addElement(item);

        ContextMenu ctxMenu = new ContextMenu();
        ctxMenu.setFor("redirectMenuItem");
        MenuModel ctxModel = new DefaultMenuModel();
        MenuItem ctxItem = new DefaultMenuItem("Remove from favorities");
        ctxModel.addElement(ctxItem);

        ctxMenu.setModel(ctxModel);

    }

(...)
}

视图:

<h:form id="leftForm">
    <p:menu id="leftMenu" model="#{leftMenuView.model}" />
</h:form>

出现菜单,但右键单击带有“重定向”标签的MenuItem时,未显示ContextMenu。

我做错了什么?

更新 - 当我添加:

uiComponent = (UIComponent) rootView
            .findComponent("leftForm");
uiComponent.getChildren().add(ctxMenu);

它在整个菜单栏上显示上下文菜单。我点击MenuItem或只是菜单组件并不重要。

当我将其更改为:

uiComponent = (UIComponent) rootView
            .findComponent(":leftForm:leftMenu:redirectMenuItem");
uiComponent.getChildren().add(ctxMenu);

我得到“java.lang.IllegalArgumentException:leftMenu”

换句话说 - 我希望得到这种行为:

<h:form>
    <p:menu id="menu_id">
        <p:menuitem id="gmail_id" value="Gmail"/>
        <p:menuitem id="hotmail_id" value="Hotmail" />
    </p:menu>
    <p:contextMenu for="gmail_id">
        <p:menuitem value="Save" />
        <p:menuitem value="Delete"/>
    </p:contextMenu>
</h:form>

但以编程方式(源代码)。

4 个答案:

答案 0 :(得分:1)

这是因为当使用DefaultMenuModel时,有效ID 您期望它们。它们被覆盖(使用浏览器开发人员工具查看生成的代码时可以看到)。

不使用DefaultMenuModel,而是使用DynamicMenuModel。与前者相反,后者允许您设置并保持 ID 分配。但是你必须在所有内容上设置id,menuitem,子菜单,组等...

可以在每个的实现中看到差异。

答案 1 :(得分:1)

我遇到了同样的问题,这就是我修复它的方法。

我尝试使用DynamicMenuModel作为@Kukeltje建议,我将自己的自定义ID添加到菜单项中。但是当我为这些ID添加contextmenu属性时,我一直在

  

页面中的异常:找不到表达式的组件&#34; leftmenu:sm_leftmenu_KEYSTORE_MANAGEMENT&#34;引自&#34; leftmenu:j_idt240:0:j_idt226&#34;。

所以我又回到DefaultMenuModel并为每个菜单添加了一个contextMenu,如:

for (AuthTransactions subMenus : subTransactions){
    String subMenuName = getI18NString(subMenus.getLabelId(),locale);
    DefaultMenuItem item = new DefaultMenuItem(
        subMenuName == null ? subMenus.getTransactionCode()
        : subMenuName);
    item.setCommand("#{" + subMenus.getViewClassName()
        + defaultMethodName + "}");
    item.setParam("menuTransactionCode",
            subMenus.getTransactionCode());
    item.setOnclick("PF('statusDialog').show()");
    item.setTitle(subMenus.getTransactionCode());
    item.setIcon(subMenus.getIcon());

    userMenuModel.addElement(item);

    ContextMenu ctxMenu = new ContextMenu();
    ctxMenu.setFor("leftmenu:"+subMenus.getTransactionCode());
    DynamicMenuModel ctxModel = new DynamicMenuModel();
    DefaultMenuItem ctxItem = new DefaultMenuItem("Add/Remove"
      +subMenus.getTransactionCode() + " to favorites","ui-icon-star");
    ctxItem.setCommand("#{currentUserManager.toggleFavorite}");
    ctxItem.setParam("menuTransactionCode", subMenus.getTransactionCode());
    ctxItem.setAjax(true);
    ctxItem.setOnclick("PF('statusDialog').show()");
    ctxItem.setUpdate(":topBar");
    ctxModel.addElement(ctxItem);
    ctxMenu.setModel(ctxModel);
    contextMenuList.add(ctxMenu);
}

但不是使用

 <ps:menu id="sm_leftmenu" model="#{userMenuModel}" stateful="false" />

我使用普通的旧HTML和<h:commandLink>

创建了菜单
<c:if test="#{userMenuModel != null and userMenuModel.elements !=null and !empty userMenuModel.elements}">
    <ul id="leftmenu:sm_leftmenu" class="layout-menubar-container">
        <c:forEach var="item" items="#{userMenuModel.elements}">
            <li id="#{item.id}" role="menuitem">
            <h:commandLink value="#{item.value}" action="#{item.command}" title="#{item.title}" onclick="PF('statusDialog').show();" id="#{item.title}">
                <f:param name="menuTransactionCode" value="#{item.title}" />
                <i class="#{item.icon} yellow" style="float:left;padding-right:5px;"></i>
            </h:commandLink>
            </li>
        </c:forEach>
    </ul>
</c:if>

并添加了下面的conext菜单,如

<h:panelGroup rendered="#{currentUserManager.contextMenuList != null and !empty currentUserManager.contextMenuList}">
    <h:dataTable value="#{currentUserManager.contextMenuList}" var="contextMenu">
        <h:column>
            <p:contextMenu binding="#{contextMenu}" model="#{contextMenu.model}" for="#{contextMenu.for}" styleClass="favCtx"/>
        </h:column>
    </h:dataTable>
 </h:panelGroup>

现在contextMenu将指向<h:commandLink>,这是一个jsf组件,正如contextMenu的PrimeFaces文档所要求的那样,并且每个菜单项都出现了contextMenu。

enter image description here

enter image description here

答案 2 :(得分:0)

您在后端设置的ID在PrimeFaces Class BaseMenuRenderer中再次被覆盖,encodeEnd()带有generateUniqueIds(..)

我为https://github.com/primefaces/primefaces/issues/1039打开了一个问题。

如果你喜欢,就提升它。似乎是PrimeFaces的bug。 它仍然存在于PrimeFaces 5.3.5

所以答案是,目前无法为MenuItem设置ID。

这是我目前设置ID的解决方法:

将它放在faces-config.xml中以使用您自己的MenuRenderer

<render-kit>
<renderer>
  <component-family>org.primefaces.component</component-family>
  <renderer-type>org.primefaces.component.MenuRenderer</renderer-type>
  <renderer-class>de.yourPackage.CustomMenuRenderer</renderer-class>
</renderer>
</render-kit>

然后创建一个像这样的CustomMenuRenderer类,只改变一点不要覆盖IDS:

public class CustomMenuRenderer extends CustomBaseMenuRenderer {
protected void encodeScript(FacesContext context, AbstractMenu abstractMenu) throws IOException {
    Menu menu = (Menu) abstractMenu;
    String clientId = menu.getClientId(context);

    WidgetBuilder wb = getWidgetBuilder(context);
    wb.initWithDomReady("PlainMenu", menu.resolveWidgetVar(), clientId)
            .attr("toggleable", menu.isToggleable(), false);

    if (menu.isOverlay()) {
        encodeOverlayConfig(context, menu, wb);
    }

    wb.finish();
}

protected void encodeMarkup(FacesContext context, AbstractMenu abstractMenu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    Menu menu = (Menu) abstractMenu;
    String clientId = menu.getClientId(context);
    String style = menu.getStyle();
    String styleClass = menu.getStyleClass();
    String defaultStyleClass = menu.isOverlay() ? Menu.DYNAMIC_CONTAINER_CLASS : Menu.STATIC_CONTAINER_CLASS;
    if (menu.isToggleable()) {
        defaultStyleClass = defaultStyleClass + " " + Menu.TOGGLEABLE_MENU_CLASS;
    }
    styleClass = styleClass == null ? defaultStyleClass : defaultStyleClass + " " + styleClass;

    writer.startElement("div", menu);
    writer.writeAttribute("id", clientId, "id");
    writer.writeAttribute("class", styleClass, "styleClass");
    if (style != null) {
        writer.writeAttribute("style", style, "style");
    }
    writer.writeAttribute("role", "menu", null);

    encodeKeyboardTarget(context, menu);

    if (menu.getElementsCount() > 0) {
        writer.startElement("ul", null);
        writer.writeAttribute("class", Menu.LIST_CLASS, null);
        encodeElements(context, menu, menu.getElements());
        writer.endElement("ul");
    }

    writer.endElement("div");
}

protected void encodeElements(FacesContext context, Menu menu, List<MenuElement> elements) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    boolean toggleable = menu.isToggleable();

    for (MenuElement element : elements) {
        if (element.isRendered()) {
            if (element instanceof MenuItem) {
                MenuItem menuItem = (MenuItem) element;
                String containerStyle = menuItem.getContainerStyle();
                String containerStyleClass = menuItem.getContainerStyleClass();
                containerStyleClass = (containerStyleClass == null) ? Menu.MENUITEM_CLASS : Menu.MENUITEM_CLASS + " " + containerStyleClass;

                if (toggleable) {
                    UIComponent parent = ((UIComponent) menuItem).getParent();
                    containerStyleClass = (parent instanceof Submenu) ? containerStyleClass + " " + Menu.SUBMENU_CHILD_CLASS : containerStyleClass;
                }

                writer.startElement("li", null);
                writer.writeAttribute("class", containerStyleClass, null);
                writer.writeAttribute("role", "menuitem", null);
                if (containerStyle != null) {
                    writer.writeAttribute("style", containerStyle, null);
                }
                if (menuItem.getId() != null) {
                    writer.writeAttribute("id", menuItem.getId(), null);
                }
                encodeMenuItem(context, menu, menuItem);
                writer.endElement("li");
            }
            else if (element instanceof Submenu) {
                encodeSubmenu(context, menu, (Submenu) element);
            }
            else if (element instanceof Separator) {
                encodeSeparator(context, (Separator) element);
            }
        }
    }
}

@SuppressWarnings("unchecked")
protected void encodeSubmenu(FacesContext context, Menu menu, Submenu submenu) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String label = submenu.getLabel();
    String icon = submenu.getIcon();
    String style = submenu.getStyle();
    String styleClass = submenu.getStyleClass();
    styleClass = styleClass == null ? Menu.SUBMENU_TITLE_CLASS : Menu.SUBMENU_TITLE_CLASS + " " + styleClass;
    boolean toggleable = menu.isToggleable();

    //title
    writer.startElement("li", null);
    if (toggleable) {
        writer.writeAttribute("id", submenu.getClientId(), null);
    }
    writer.writeAttribute("class", styleClass, null);
    if (style != null) {
        writer.writeAttribute("style", style, null);
    }

    if (menu.getId() != null) {
        writer.writeAttribute("id", menu.getId(), null);
    }

    writer.startElement("h3", null);

    if (menu.isToggleable()) {
        encodeIcon(context, label, Menu.EXPANDED_SUBMENU_HEADER_ICON_CLASS);
    }

    if (icon != null) {
        encodeIcon(context, label, "ui-submenu-icon ui-icon " + icon);
    }

    if (label != null) {
        writer.writeText(label, "value");
    }

    writer.endElement("h3");

    writer.endElement("li");

    encodeElements(context, menu, submenu.getElements());
}

protected void encodeIcon(FacesContext context, String label, String styleClass) throws IOException {
    ResponseWriter writer = context.getResponseWriter();

    writer.startElement("span", null);
    writer.writeAttribute("class", styleClass, null);
    writer.endElement("span");
}

}

最后,您还需要自己的CustomBaseMenuRenderer类:

public class CustomBaseMenuRenderer extends BaseMenuRenderer {
@Override
protected void encodeMenuItem(FacesContext context, AbstractMenu menu, MenuItem menuitem) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String title = menuitem.getTitle();
    String style = menuitem.getStyle();
    boolean disabled = menuitem.isDisabled();

    writer.startElement("a", null);
    writer.writeAttribute("tabindex", "-1", null);
    if (shouldRenderId(menuitem)) {
        writer.writeAttribute("id", menuitem.getClientId(), null);
    }
    if (title != null) {
        writer.writeAttribute("title", title, null);
    }

    String styleClass = this.getLinkStyleClass(menuitem);
    if (disabled) {
        styleClass = styleClass + " ui-state-disabled";
    }

    writer.writeAttribute("class", styleClass, null);

    if (style != null) {
        writer.writeAttribute("style", style, null);
    }

    if (disabled) {
        writer.writeAttribute("href", "#", null);
        writer.writeAttribute("onclick", "return false;", null);
    }
    else {
        setConfirmationScript(context, menuitem);
        String onclick = menuitem.getOnclick();

        //GET
        if (menuitem.getUrl() != null || menuitem.getOutcome() != null) {
            String targetURL = getTargetURL(context, (UIOutcomeTarget) menuitem);
            writer.writeAttribute("href", targetURL, null);

            if (menuitem.getTarget() != null) {
                writer.writeAttribute("target", menuitem.getTarget(), null);
            }
        }
        //POST
        else {
            writer.writeAttribute("href", "#", null);

            UIComponent form = ComponentTraversalUtils.closestForm(context, menu);
            if (form == null) {
                throw new FacesException("MenuItem must be inside a form element");
            }

            String command;
            if (menuitem.isDynamic()) {
                String menuClientId = menu.getClientId(context);
                Map<String, List<String>> params = menuitem.getParams();
                if (params == null) {
                    params = new LinkedHashMap<String, List<String>>();
                }
                List<String> idParams = new ArrayList<String>();
                idParams.add(menuitem.getId());
                params.put(menuClientId + "_menuid", idParams);

                command = menuitem.isAjax() ? buildAjaxRequest(context, menu, (AjaxSource) menuitem, form, params) : buildNonAjaxRequest(context, menu, form, menuClientId, params, true);
            }
            else {
                command = menuitem.isAjax() ? buildAjaxRequest(context, (AjaxSource) menuitem, form)
                        : buildNonAjaxRequest(context, ((UIComponent) menuitem), form, ((UIComponent) menuitem).getClientId(context), true);
            }

            onclick = (onclick == null) ? command : onclick + ";" + command;
        }

        if (onclick != null) {
            if (menuitem.requiresConfirmation()) {
                writer.writeAttribute("data-pfconfirmcommand", onclick, null);
                writer.writeAttribute("onclick", menuitem.getConfirmationScript(), "onclick");
            }
            else {
                writer.writeAttribute("onclick", onclick, null);
            }
        }
    }

    encodeMenuItemContent(context, menu, menuitem);

    writer.endElement("a");
}

@Override
protected boolean shouldRenderId(MenuElement element) {
    if (element instanceof UIComponent) {
        return shouldWriteId((UIComponent) element);
    }
        return false;
}

@Override
protected void encodeMarkup(FacesContext context, AbstractMenu abstractMenu) throws IOException {
    // TODO Auto-generated method stub

}

@Override
protected void encodeScript(FacesContext context, AbstractMenu abstractMenu) throws IOException {
    // TODO Auto-generated method stub

}
}

我现在使用它已经2年了,它就像一个魅力。 但很难过,他们还没有解决它。

答案 3 :(得分:-1)

请看一下页面:

PrimeFaces - 新的MenuModel。

简单示例:

MenuModel model = new DefaultMenuModel();

DefaultSubMenu subMenu = new DefaultSubMenu("some submenu");
DefaultMenuItem menuItem = new DefaultMenuItem("some item");
subMenu.addElement(menuItem);

model.addElement(subMenu);

model.generateUniqueIds();

方法generateUniqueIds()将使您免于自己创建和设置id字符串。