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>
问题分析
- 对话框未包含在表单内:JSF组件需要在
<h:form>中才能参与请求的视图状态同步,对话框里的inputText不在表单中,Ajax无法将服务器端的selectedNode值同步到客户端组件。 - Ajax未指定更新目标:当前的
<p:ajax>只触发了Bean的方法,但没有告诉JSF要重新渲染对话框组件,导致客户端的inputText还是旧的空值。
解决方案
修改index.xhtml,做两处调整:
- 给对话框添加
id,并在对话框内部添加<h:form>(避免表单嵌套问题,PrimeFaces推荐对话框内部放独立表单)。 - 在
<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
相关产品推荐
相关产品推荐

