Primefaces 树动态上下文菜单

Primefaces tree dynamic contextmenu

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

我已经使用支持 bean 模型实现了它,并且在每个 selection 发生变化时更新它。一切正常,除了如果用户右键单击未 selected 节点会发生以下情况:

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

但这对树不起作用。

欢迎任何提示


PF 6.1.2,WF 10.0.0

我想我找到了可行的解决方案。我已经根据 primeface 的 showcase

构建了一个示例

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);
        }
    });

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,但可能也适用于更高版本。