You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Faces问题:PrimeFaces对话框值表达式绑定内容为空

问题描述

我正在学习Java Faces(Java EE体系),写了一个用PrimeFaces树和对话框组件的示例,需求是选择树的条目后,在对话框展示对应数据的详情。

目前树组件正常显示,选中条目能触发Ajax调用Bean的onSelect方法,对话框也能弹出,但对话框里的inputText内容是空的。通过System.out.println验证,服务器端的selectedNode对象有效,属性已经填充了选中条目的数据,但浏览器网络面板显示Ajax没有把更新后的值传回客户端。

原代码

TestClass.java

public class TestClass {
    private String name;
    private String otherAttribute;
    
    public TestClass(String name, String otherAttribute) {
        this.name = name;
        this.otherAttribute = otherAttribute;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOtherAttribute() {
        return otherAttribute;
    }

    public void setOtherAttribute(String otherAttribute) {
        this.otherAttribute = otherAttribute;
    }
    
    public String toString() {
        return new String("TestClass(name=" + this.name + ", otherAttribute=" + this.otherAttribute + ")");
    }
}

TestService.java

@Named
@ApplicationScoped
public class TestService {
    public TreeNode<TestClass> createTestTree() {
        TreeNode<TestClass> root = new DefaultTreeNode<TestClass>(new TestClass("root", null), null);
        
        TreeNode<TestClass> entry1 = new DefaultTreeNode<TestClass>(new TestClass("entry1", "attribute of entry1"), root);
        TreeNode<TestClass> entry2 = new DefaultTreeNode<TestClass>(new TestClass("entry2", "attribute of entry2"), root);
        TreeNode<TestClass> entry3 = new DefaultTreeNode<TestClass>(new TestClass("entry3", "attribute of entry3"), root);
        
        TreeNode<TestClass> subentry11 = new DefaultTreeNode<TestClass>(new TestClass("subentry11", "subentry 1 attribute of entry1"), entry1);
        TreeNode<TestClass> subentry12 = new DefaultTreeNode<TestClass>(new TestClass("subentry12", "subentry 2 attribute of entry1"), entry1);
        TreeNode<TestClass> subentry13 = new DefaultTreeNode<TestClass>(new TestClass("subentry13", "subentry 3 attribute of entry1"), entry1);
        
        TreeNode<TestClass> subentry21 = new DefaultTreeNode<TestClass>(new TestClass("subentry21", "subentry 1 attribute of entry2"), entry2);
        TreeNode<TestClass> subentry22 = new DefaultTreeNode<TestClass>(new TestClass("subentry22", "subentry 2 attribute of entry2"), entry2);
        
        TreeNode<TestClass> subentry31 = new DefaultTreeNode<TestClass>(new TestClass("subentry31", "subentry 1 attribute of entry3"), entry3);
        
        return root;
    }
}

TestBean.java

@Named("testBean")
@ViewScoped
public class TestBean implements Serializable {
    
    private static final long serialVersionUID = -4448740441931801702L;

    @Inject
    TestService treeService;
    
    private TreeNode<TestClass> root;
    private TestClass selectedNode;
    
    @PostConstruct
    private void init() {
        this.root = this.treeService.createTestTree();
    }

    public void onTreeNodeSelect(NodeSelectEvent event) {
        System.out.println("NodeSelectEvent: " + event);
        this.selectedNode = (TestClass)event.getTreeNode().getData();
        System.out.println("Selected Node" + this.selectedNode);
        System.out.println("Showing dialog");
        PrimeFaces.current().executeScript("PF('treeNodeEditor').show()");
    }

    public TreeNode<TestClass> getRoot() {
        return root;
    }

    public void setRoot(TreeNode<TestClass> root) {
        this.root = root;
    }

    public TestClass getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(TestClass selectedNode) {
        this.selectedNode = selectedNode;
    }
}

index.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
                xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Tree test</title>
    </h:head>
    <h:body>
        <div class="grid">
            <div class="col-12">
                <h:form>    
                    <p:tree value="#{testBean.root}" var="node" selectionMode="single">
                        <p:ajax event="select" listener="#{testBean.onTreeNodeSelect}"></p:ajax>
                        <p:treeNode>
                            <h:outputText value="#{node.name}"></h:outputText>
                        </p:treeNode>
                    </p:tree>
                </h:form>
            </div>
        </div>
        <p:dialog widgetVar="treeNodeEditor">
            <div class="grid">
                <div class="col-6">
                    <p:outputLabel for="@next" value="Name"></p:outputLabel>
                    <p:inputText style="width: 100%" value="#{testBean.selectedNode.name}"></p:inputText>
                </div>
                <div class="col-6">
                    <p:outputLabel for="@next" value="Attribute"></p:outputLabel>
                    <p:inputText style="width: 100%" value="#{testBean.selectedNode.otherAttribute}"></p:inputText>
                </div>
            </div>
        </p:dialog>
    </h:body>
</html>
问题分析
  1. 对话框未包含在表单内:JSF组件需要在<h:form>中才能参与请求的视图状态同步,对话框里的inputText不在表单中,Ajax无法将服务器端的selectedNode值同步到客户端组件。
  2. Ajax未指定更新目标:当前的<p:ajax>只触发了Bean的方法,但没有告诉JSF要重新渲染对话框组件,导致客户端的inputText还是旧的空值。
解决方案

修改index.xhtml,做两处调整:

  1. 给对话框添加id,并在对话框内部添加<h:form>(避免表单嵌套问题,PrimeFaces推荐对话框内部放独立表单)。
  2. 在<p:ajax>中添加update属性,指定要更新的对话框ID,让Ajax请求后重新渲染对话框内容。

修改后的index.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
                xmlns:p="http://primefaces.org/ui">
    <h:head>
        <title>Tree test</title>
    </h:head>
    <h:body>
        <div class="grid">
            <div class="col-12">
                <h:form>    
                    <p:tree value="#{testBean.root}" var="node" selectionMode="single">
                        <!-- 添加update属性,指定要更新的对话框ID -->
                        <p:ajax event="select" listener="#{testBean.onTreeNodeSelect}" update="treeNodeEditor"></p:ajax>
                        <p:treeNode>
                            <h:outputText value="#{node.name}"></h:outputText>
                        </p:treeNode>
                    </p:tree>
                </h:form>
            </div>
        </div>
        <!-- 给对话框添加id,内部添加h:form -->
        <p:dialog widgetVar="treeNodeEditor" id="treeNodeEditor">
            <h:form>
                <div class="grid">
                    <div class="col-6">
                        <p:outputLabel for="@next" value="Name"></p:outputLabel>
                        <p:inputText style="width: 100%" value="#{testBean.selectedNode.name}"></p:inputText>
                    </div>
                    <div class="col-6">
                        <p:outputLabel for="@next" value="Attribute"></p:outputLabel>
                        <p:inputText style="width: 100%" value="#{testBean.selectedNode.otherAttribute}"></p:inputText>
                    </div>
                </div>
            </h:form>
        </p:dialog>
    </h:body>
</html>

额外说明

  • 对话框内部添加表单是因为如果对话框在外部表单里,可能会导致表单嵌套(JSF不允许表单嵌套),独立表单更安全。
  • update="treeNodeEditor"会让Ajax请求完成后重新渲染整个对话框,这样对话框内的inputText就会从服务器端获取最新的selectedNode属性值。
  • Bean的代码不需要修改,因为服务器端已经正确设置了selectedNode,只是需要把这个值同步到客户端视图。

内容的提问来源于stack exchange,提问作者audacity363

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:42:01