Primefaces 树动态上下文菜单
Primefaces tree dynamic contextmenu
我需要一个树的动态上下文菜单(基于某些权限,每个节点都不同,所以我不能为每个节点的类型使用不同的菜单)。
我已经使用支持 bean 模型实现了它,并且在每个 selection 发生变化时更新它。一切正常,除了如果用户右键单击未 selected 节点会发生以下情况:
- 显示上下文菜单(之前 selected 节点)
- 上下文菜单已更新(由 select 事件触发)并再次隐藏
- 再次右击显示更新后的上下文菜单
我找到了 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,但可能也适用于更高版本。
我需要一个树的动态上下文菜单(基于某些权限,每个节点都不同,所以我不能为每个节点的类型使用不同的菜单)。
我已经使用支持 bean 模型实现了它,并且在每个 selection 发生变化时更新它。一切正常,除了如果用户右键单击未 selected 节点会发生以下情况:
- 显示上下文菜单(之前 selected 节点)
- 上下文菜单已更新(由 select 事件触发)并再次隐藏
- 再次右击显示更新后的上下文菜单
我找到了 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,但可能也适用于更高版本。