【问题标题】:Primefaces tree dynamic contextmenuPrimefaces 树动态上下文菜单
【发布时间】:2017-11-10 03:53:03
【问题描述】:

我需要一个树的动态上下文菜单(根据某些权限在每个节点上有所不同,所以我不能为每个节点的类型使用不同的菜单)。

我使用支持 bean 模型实现了它,并在每次选择更改时更新它。一切正常,除了如果用户右键单击未选择的节点会发生以下情况:

  • 显示上下文菜单(先前选择的节点)
  • 上下文菜单已更新(由选择事件触发),然后再次隐藏
  • 再次右键单击显示更新的上下文菜单

我找到了treetable的解决方法 https://dnhome.wordpress.com/2013/10/07 ... e-of-tree/

但它不适用于树。

欢迎任何提示


PF 6.1.2,WF 10.0.0

【问题讨论】:

    标签: primefaces jsf-2 tree contextmenu


    【解决方案1】:

    Neo 的回答是正确的,但有点过于冗长,并且会覆盖页面上的所有上下文菜单,而不是特定的。这是一个带有解释的更简洁的版本。

    <p:tree id="tree">
        <!-- when showing the menu, update it first and then call show a second time -->
        <p:ajax event="contextMenu" update="@parent-ctx_menu"
            oncomplete="PF('ctxMenuVar').show()" />
    </p:tree>
    
    <p:contextMenu id="ctx_menu" for="tree" widgetVar="ctxMenuVar" />
    
    <script>
        $(function() {
            var ctxMenuWidget = PF('ctxMenuVar');
            (function(orig) {
                ctxMenuWidget.show = function(e) {
                    if (e) {
                        // this is the first show call by PrimeFaces - let's remember the
                        // argument event, and wait for the update
                        this.lastShowEvent = e;
                        e.preventDefault();
                    } else {
                        // this is our second show call from oncomplete - the menu is updated,
                        // time to actually show it
                        checkState(this.lastShowEvent);
                        orig.call(this, this.lastShowEvent);
                    }
                };
            })(ctxMenuWidget.show);
        });
    </script>
    

    这至少适用于 PrimeFaces 6.1,但也可能适用于更高版本。

    【讨论】:

      【解决方案2】:

      我想我找到了一个可行的解决方案。我已经建立了一个基于primeface的展示的例子

      BasicView.java

      package example;
      
      import java.io.Serializable;
      import javax.annotation.PostConstruct;
      import javax.faces.bean.ManagedBean;
      import javax.faces.bean.ManagedProperty;
      import javax.faces.bean.ViewScoped;
      
      import org.primefaces.context.RequestContext;
      import org.primefaces.event.NodeSelectEvent;
      import org.primefaces.model.DefaultTreeNode;
      import org.primefaces.model.TreeNode;
      
      @ManagedBean(name="treeBasicView")
      @ViewScoped
      public class BasicView implements Serializable {
      
          private TreeNode root;
      
          @ManagedProperty(value="#{selectionView}")
          SelectionView selectionView;
      
          @PostConstruct
          public void init() {
              root = new DefaultTreeNode("Root", null);
              TreeNode node0 = new DefaultTreeNode("Node 0", root);
              TreeNode node1 = new DefaultTreeNode("Node 1", root);
      
              TreeNode node00 = new DefaultTreeNode("Node 0.0", node0);
              TreeNode node01 = new DefaultTreeNode("Node 0.1", node0);
      
              TreeNode node10 = new DefaultTreeNode("Node 1.0", node1);
      
              node1.getChildren().add(new DefaultTreeNode("Node 1.1"));
              node00.getChildren().add(new DefaultTreeNode("Node 0.0.0"));
              node00.getChildren().add(new DefaultTreeNode("Node 0.0.1"));
              node01.getChildren().add(new DefaultTreeNode("Node 0.1.0"));
              node10.getChildren().add(new DefaultTreeNode("Node 1.0.0"));
              root.getChildren().add(new DefaultTreeNode("Node 2"));
          }
      
          public TreeNode getRoot() {
              return root;
          }
      
          public void onNodeSelect(NodeSelectEvent event){
              checkSelectionChanged();
          }
      
          public void onRightClickSelectListener(NodeSelectEvent e) {
                  checkSelectionChanged();
              }
      
      /* Update the contextMenu only if the selection has changed */
              private void checkSelectionChanged(){
                  if(this.getSelectionView().getSelectedNode()!=this.getSelectionView().getPrevSelectedNode()){
                  RequestContext.getCurrentInstance().update("treeform:mytreeContexMenu");
              }
          }
      
          public SelectionView getSelectionView(){
              return selectionView;
          }
      
          public void setSelectionView(SelectionView selectionView){
              this.selectionView=selectionView;
          }
      }
      

      MenuView.java

      package example;
      
      import javax.annotation.PostConstruct;
      import javax.faces.application.FacesMessage;
      import javax.faces.bean.ManagedBean;
      import javax.faces.bean.ManagedProperty;
      import javax.faces.bean.RequestScoped;
      import javax.faces.context.FacesContext;
      
      import org.primefaces.model.menu.*;
      
      @ManagedBean
      @RequestScoped
      public class MenuView {
      
          private MenuModel model;
      
          @ManagedProperty(value="#{selectionView}")
          SelectionView selectionView;
      
      
          public void init() {
              model = new DefaultMenuModel();
      
              //First submenu
              DefaultSubMenu firstSubmenu = new DefaultSubMenu("Dynamic Submenu");
      
              DefaultMenuItem item = new DefaultMenuItem("External");
              item.setUrl("http://www.primefaces.org");
              item.setIcon("ui-icon-home");
              firstSubmenu.addElement(item);
      
              model.addElement(firstSubmenu);
      
              //Second submenu
              DefaultSubMenu secondSubmenu = new DefaultSubMenu("Dynamic Actions");
      
              this.getTreeSelectionView().getSelectedNode();
              if(this.getTreeSelectionView().getSelectedNode()==null){
                  return;
              }
              String nodeText = this.getTreeSelectionView().getSelectedNode().getData().toString();
      
              if(nodeText.contains("0")){ //just a condition
                  item=new DefaultMenuItem("Action for 0");
                  item.setIcon("ui-icon-disk");
                  item.setCommand("#{menuView.save}");
                  secondSubmenu.addElement(item);
              }
      
              if(nodeText.contains("1")){ //just a condition
                  item=new DefaultMenuItem("Action for 1");
                  item.setIcon("ui-icon-close");
                  item.setCommand("#{menuView.delete}");
                  item.setAjax(false);
                  secondSubmenu.addElement(item);
              }
      
              if(nodeText.contains("2")){ //just a condition
                  item=new DefaultMenuItem("Action for 2");
                  item.setIcon("ui-icon-search");
                  item.setCommand("#{menuView.redirect}");
                  secondSubmenu.addElement(item);
              }
      
              model.addElement(secondSubmenu);
          }
      
          public MenuModel getModel() {
              if(model==null){
                  this.init();
              }
              return model;
          }
      
          public void save() {}
      
          public void update() {}
      
          public void delete() {}
      
      
          public SelectionView getTreeSelectionView(){
              return selectionView;
          }
      
          public void setSelectionView(SelectionView selectionView){
              this.selectionView=selectionView;
          }
      }
      

      SelectionView.java

      package example;
      
      import java.io.Serializable;
      import javax.annotation.PostConstruct;
      import javax.faces.application.FacesMessage;
      import javax.faces.bean.ManagedBean;
      import javax.faces.bean.ManagedProperty;
      import javax.faces.bean.ViewScoped;
      import javax.faces.context.FacesContext;
      import org.primefaces.model.TreeNode;
      
      @ManagedBean(name="selectionView")
      @ViewScoped
      public class SelectionView implements Serializable {
      
      
          private TreeNode selectedNode;
          private TreeNode prevSelectedNode;
      
      
          public TreeNode getSelectedNode() {
              return selectedNode;
          }
      
          public void setSelectedNode(TreeNode selectedNode) {
              this.setPrevSelectedNode(this.selectedNode);
              this.selectedNode = selectedNode;
          }
      
          public TreeNode getPrevSelectedNode(){
              return prevSelectedNode;
          }
      
          public void setPrevSelectedNode(TreeNode prevSelectedNode){
              this.prevSelectedNode=prevSelectedNode;
          }
      }
      

      tree.xhtml

      <!DOCTYPE html>
              <html
                      xmlns="http://www.w3.org/1999/xhtml"
                      xmlns:ui="http://java.sun.com/jsf/facelets"
                      xmlns:f="http://java.sun.com/jsf/core"
                      xmlns:h="http://java.sun.com/jsf/html"
                      xmlns:p="http://primefaces.org/ui">
      
              <h:head>
                  <title><h:outputText value="Test tree"/></title>
                  <h:outputScript library="scripts" name="navigator.js"/>
      
              </h:head>
              <h:body>
      
                  <h:form id="treeform">
      
                  <p:tree value="#{treeBasicView.root}" var="node" dynamic="true" id="mytree" selectionMode="single" selection="#{selectionView.selectedNode}">
                      <p:ajax event="select" listener="#{treeBasicView.onNodeSelect}"/>
                      <p:ajax event="contextMenu" listener="#{treeBasicView.onRightClickSelectListener}" oncomplete="PF('mytreeContexMenuVar').show()"/>
      
                      <p:treeNode>
                          <h:outputText value="#{node}" />
                      </p:treeNode>
                  </p:tree>
      
                  <p:contextMenu for="mytree" id="mytreeContexMenu" model="#{menuView.model}" widgetVar="mytreeContexMenuVar">
      
                  </p:contextMenu>
                  </h:form>
              </h:body>
      </html>
      

      mytree.js contextMenu 应该更新,而不是显示 (基于此处找到的示例:https://dnhome.wordpress.com/2013/10/07

          var siteFunctions = {
              //patch to fix a problem that the context menu disappears after update
              //delay the show to occure after the update
              patchContextMenuShow: function() {
                  'use strict';
                  var protShow = PrimeFaces.widget.ContextMenu.prototype.show;
                  siteFunctions.patchContextMenuShow.lastEvent = null;
                  PrimeFaces.widget.ContextMenu.prototype.show = function(e) {
                      var ret;
                      if (e) {
                          siteFunctions.patchContextMenuShow.lastEvent = e;
                          siteFunctions.patchContextMenuShow.lastEventArg = arguments;
      
                           siteFunctions.patchContextMenuShow.lastEventContext = this;
                          //prevent default browser context menu
                           e.preventDefault();
                          e.stopPropagation();
                      } else if (siteFunctions.patchContextMenuShow.lastEvent) {
      
                          ret = protShow.apply(siteFunctions.patchContextMenuShow.lastEventContext, siteFunctions.patchContextMenuShow.lastEventArg);
                          siteFunctions.patchContextMenuShow.lastEvent = null;
                      }
                      return ret;
                  };
              }
          };
      
          $(document).ready(function() {
              'use strict';
              try {
                  siteFunctions.patchContextMenuShow();
              } catch (e) {
                  console.error(e);
              }
          });
      

      【讨论】:

      • 您发布了很多代码,但没有解释代码中的重要部分。您确定不能将代码简化为更精简但功能齐全的示例 (minimal reproducible example)?
      • 我知道这是很多代码,但如果我减少它,这个例子就不再完整了。通过这种方式,很容易重现和理解。源码中直接添加了一些解释
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多