为什么'p:ajax update'无法更新p:tree?

时间:2018-05-10 17:43:27

标签: primefaces

我希望产生这样的结果:每当展开树节点时,任何先前展开的兄弟节点都会被折叠。

以下是我的页面和我的bean。扩展节点时调用onNodeExpanded方法; System.out语句产生预期的结果。但是,执行setExpanded(false)的节点仍然在页面上展开。

我添加了一个commandButton,以便我可以强制更新树。按下它会导致页面正确显示先前由ajax事件侦听器折叠的节点。

Page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:p="http://primefaces.org/ui">

    <h:head>
        <title>PrimeFaces Tree: Ajax Update Problem</title>
    </h:head>
    <h:body> 
        <h3>PrimeFaces Tree: Ajax Update Problem</h3>

        <h:form>
            <p:tree id="tree" value="#{problemController.root}" var="node" dynamic="true" orientation="horizontal">
                <p:treeNode>
                    <h:outputText value="#{node}" />
                </p:treeNode>

                <p:ajax event="expand" listener="#{problemController.onNodeExpanded}" update="tree" />
            </p:tree>

            <h:commandButton value="Update" immediate="true">
                <f:ajax render="tree" />
            </h:commandButton>
        </h:form>
    </h:body>
</html>

The Bean:

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;

import org.primefaces.event.NodeExpandEvent;
import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;

@Named
@ViewScoped
public class ProblemController implements Serializable {

    private static final long serialVersionUID = 1L;
    private TreeNode root;

    @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 onNodeExpanded(NodeExpandEvent event) {
        TreeNode expandedNode = event.getTreeNode();
        System.out.printf("Expanded %s\n", expandedNode);

        TreeNode parent = expandedNode.getParent();
        if (parent != null) {
            for (TreeNode sibling : parent.getChildren()) {
                if (sibling.isExpanded() && sibling != expandedNode) {
                    sibling.setExpanded(false);
                    System.out.printf("Collapsed %s\n", sibling);
                }
            }
        }
    }
}

Java 1.8 PrimeFaces 6.1 Mojarra 2.3 Weld 3.0

更新1

我已使用@Kukeltje提供的代码更新了页面。不幸的是,扩展节点的兄弟节点没有被折叠。我在closeOthers函数中添加了一条记录到控制台的语句:显示我展开的任何节点的数据(“root”,“0”或“0_1”等)(如预期的那样)。

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:p="http://primefaces.org/ui">

<h:head>
    <title>PrimeFaces Tree</title>
</h:head>
<h:body> 
    <h3>PrimeFaces Tree</h3>

    <h:form>
        <p:tree id="tree" widgetVar="treeW" value="#{testController.root}" var="node" orientation="horizontal" selectionMode="single">
            <p:treeNode>
                <h:outputText value="#{node}" />
            </p:treeNode>

            <p:ajax event="expand" listener="#{testController.onNodeExpanded}" oncomplete="closeOthers(this, PF('treeW'))"/>
        </p:tree>

        <h:outputScript>

        //<![CDATA[

        function closeOthers(current, widget) {

            var dataRowkey = $.urlParam(decodeURIComponent(current.data),current.source+"_expandNode");
            var selector = "li[data-rowkey='"+dataRowkey+"']";
            console.log("Collapsing siblings of " + selector);
            $(widget.jq).find(selector).siblings().find("span[aria-expanded='true']").siblings(".ui-tree-toggler").trigger("click");

        }


         $.urlParam = function (query, name) {
            var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(query);
            return (results !== null) ? results[1] || 0 : false;
         }

        //]]>

        </h:outputScript>

        <h:commandButton value="Update" immediate="true">
            <f:ajax render="tree" />
        </h:commandButton>

    </h:form>
</h:body>
</html>

1 个答案:

答案 0 :(得分:1)

从组件内部的ajax调用中更新完整组件并不常见。由于您使用dynamic="true",因此与不使用dynamic="true"相比,您看到的下行影响更小。如果您更新p:tree周围的容器,也会发生同样的情况。树上实际上有2个更新,彼此之间存在冲突,特别是当有一些额外的节点隐藏时。

您实际上似乎想要实现的是通过使用树的服务器端操作(这本身没有错误)一次只打开一个节点。这确实与https://github.com/primefaces/primefaces/issues/2277中所尝试的内容有关,但该问题的制定过于狭窄,因为它不是关于选择/取消选择或者您喜欢隐藏/取消隐藏,而是更普遍的问题,即无法更新组件中ajax调用中的完整树组件。

更新服务器端的东西,以便仍然支持完全刷新仍应该完成。但是,您可以尝试使用update="tree" javascript回调来关闭客户端的所有其他节点,而不是执行oncomplete

添加以下javascript将为您完成此操作:

<h:outputScript>

//<![CDATA[

function closeOthers(current, widget) {

    var dataRowkey = $.urlParam(decodeURIComponent(current.data),current.source+"_expandNode");


    //For a vertical tree:      
    //var selector = "li[data-rowkey='"+dataRowkey+"']";
    //$(widget.jq).find(selector).siblings().find("span[aria-expanded='true']").siblings(".ui-tree-toggler").trigger("click");


    //For a horizontal tree
    var selector = "td[data-rowkey='"+dataRowkey+"']";

    // The first attempt was to look for aria-expanded=true but we
    // found that sometimes it would be present on a collapsed node (bug?).
    // $(widget.jq).find(selector).parent().parent().parent().siblings().find("td[aria-expanded='true']").find(".ui-tree-toggler").trigger("click");

    // Searching instead for the minus icon did the trick
    $(widget.jq).find(selector).parent().parent().parent().siblings().find(".ui-icon-minus").trigger("click");

}


 $.urlParam = function (query, name) {
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(query);
    return (results !== null) ? results[1] || 0 : false;
 }

//]]>
</h:outputScript>

然后

  • 为您的小部件添加一个widgetVar,例如widgetVar="treeW"
  • p:ajax中添加一个oncomplete,如此oncomplete="closeOthers(this, PF('treeW'))"
  • 删除 dynamic="true" 或ADD cache="false"(我发现错误,然后每个节点只能运行一次,https://github.com/primefaces/primefaces/issues/3668
  • 删除 update="tree"

所以html最终会像:

<p:tree id="tree" widgetVar="treeW" value="#{problemController.root}" var="node" orientation="horizontal">
    <p:treeNode>
        <h:outputText value="#{node}" />
    </p:treeNode>

    <p:ajax event="expand" listener="#{problemController.onNodeExpanded}" oncomplete="closeOthers(this, PF('treeW'))"/>
</p:tree>