【问题标题】:Primefaces - How to dynamically add a ContextMenu to a MenuItemPrimefaces - 如何将 ContextMenu 动态添加到 MenuItem
【发布时间】:2015-04-03 07:16:44
【问题描述】:

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

托管豆:

@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 或只是 Menu 组件无关紧要。

当我将其更改为:

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>

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

【问题讨论】:

  • 你无法将上下文菜单添加到视图中
  • 似乎 item.setId("redirectMenuItem"); 没有将 id 设置为 &lt;li class="ui-menuitem ui-widget ui-corner-all" role="menuitem"&gt;

标签: primefaces menu contextmenu menuitem


【解决方案1】:

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

按照@Kukeltje 的建议,我尝试使用DynamicMenuModel,并将我自己的自定义ID 添加到菜单项中。但是当我为这些 id 添加 contextmenu 属性时,我不断得到

页面异常:找不到从“leftmenu:j_idt240:0:j_idt226”引用的表达式“leftmenu:sm_leftmenu_KEYSTORE_MANAGEMENT”的组件。

所以我恢复到DefaultMenuModel 并为everyMenuItem 添加了一个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 和 &lt;h:commandLink&gt; 创建了菜单

<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 将指向 &lt;h:commandLink&gt;,这是一个 jsf 组件,这是 PrimeFaces 文档要求的 contextMenu 和每个出现的菜单项的 contextMenu。

和

【讨论】:

    【解决方案2】:

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

    不要使用DefaultMenuModel,而是使用DynamicMenuModel。与前者相反,后者允许您设置并保留 id 的您 分配。但是你必须在所有东西上设置 id,菜单项、子菜单、组等......

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

    【讨论】:

      【解决方案3】:

      您在 Backend 中设置的 Id 在 PrimeFaces 类 BaseMenuRenderer 中再次被覆盖,encodeEnd() 和 generateUniqueIds(..)

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

      如果您愿意,请点赞。似乎是一个 PrimeFaces 错误。 它仍然存在于 PrimeFaces 5.3.5

      所以答案是,目前无法为 MenuItems 设置 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 年了,它就像一个魅力。 但是很遗憾,他们还没有修复它。

      【讨论】:

      • 请用版本信息等改进github中的问题...以及Bug...错误...小的遗漏是一个更好的术语
      • 刚刚对此进行了更深入的调查,也是因为我发现了 Cagatay 本人的一篇帖子。这是设计的,而不是错误。查看我的其他答案
      • 这绝对是 primefaces 6.1 中仍然存在的错误我现在添加了一个解决方法,对我来说效果很好。
      【解决方案4】:

      请看一下页面:

      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 字符串。

      【讨论】:

      • 但是在这种情况下,您不知道菜单项的 id 预先,并且您不能在上下文菜单上设置'for'属性,所以上下文菜单将 not 工作,因此这个答案并不完全正确。除非您保留对特定 menuItem 的引用并在 调用 model.generateUniqueIds() 之后从中读取 ID。请将其添加到您的答案中!请在我的回答中添加(添加的)介绍。澄清为什么总是好的
      猜你喜欢
      • 2013-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-07
      • 2013-03-23
      相关资源
      最近更新 更多