ajax 侦听器不起作用
•浏览 1
ajax listener inside conditionally rendered element not working
本问题已经有最佳答案,请猛点这里访问。
这是我的问题的测试用例:
测试豆:
@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = -2329929006490721388L;
private List<TestObject> testObjects;
private int selectedId;
public TestBean(){
List<TestObject> to = new ArrayList<TestObject>();
for(int i=0; i<10; i++){
TestObject o = new TestObject(i,"object-"+i);
to.add(o);
}
this.setTestObjects(to);
}
public void testAjaxListener(int id){
System.out.println("testAjaxListener("+id+")");
this.setSelectedId(id);
}
//+getters/setters
}public class TestObject {
private int id;
private String name;
public TestObject(int id, String name){
this.setId(id);
this.setName(name);
}
//+getters/setters
}<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head></h:head>
<h:body>
<h:form id="testForm">
<h:panelGroup rendered="#{param['view'] eq 'test'}">
DataTable
<h:dataTable var="o" value="#{testBean.testObjects}">
<h:column>
<h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}">
<f:ajax
render=":testForm:outputTest"
/>
</h:commandLink>
</h:column>
</h:dataTable>
output
<h:outputText id="outputTest" value="#{testBean.selectedId}" />
</h:panelGroup>
</h:form>
</h:body>
</html><f:metadata>
<f:viewParam name="view" value="#{testBean.view}" />
</f:metadata>
...
<h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...>
<f:param name="view" value="#{param.view}" />
...
</h:commandLink><o:form includeRequestParams="true">
...
</o:form>
测试对象
@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = -2329929006490721388L;
private List<TestObject> testObjects;
private int selectedId;
public TestBean(){
List<TestObject> to = new ArrayList<TestObject>();
for(int i=0; i<10; i++){
TestObject o = new TestObject(i,"object-"+i);
to.add(o);
}
this.setTestObjects(to);
}
public void testAjaxListener(int id){
System.out.println("testAjaxListener("+id+")");
this.setSelectedId(id);
}
//+getters/setters
}public class TestObject {
private int id;
private String name;
public TestObject(int id, String name){
this.setId(id);
this.setName(name);
}
//+getters/setters
}<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head></h:head>
<h:body>
<h:form id="testForm">
<h:panelGroup rendered="#{param['view'] eq 'test'}">
DataTable
<h:dataTable var="o" value="#{testBean.testObjects}">
<h:column>
<h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}">
<f:ajax
render=":testForm:outputTest"
/>
</h:commandLink>
</h:column>
</h:dataTable>
output
<h:outputText id="outputTest" value="#{testBean.selectedId}" />
</h:panelGroup>
</h:form>
</h:body>
</html><f:metadata>
<f:viewParam name="view" value="#{testBean.view}" />
</f:metadata>
...
<h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...>
<f:param name="view" value="#{param.view}" />
...
</h:commandLink><o:form includeRequestParams="true">
...
</o:form>
测试.xhtml:
@ManagedBean
@ViewScoped
public class TestBean implements Serializable {
private static final long serialVersionUID = -2329929006490721388L;
private List<TestObject> testObjects;
private int selectedId;
public TestBean(){
List<TestObject> to = new ArrayList<TestObject>();
for(int i=0; i<10; i++){
TestObject o = new TestObject(i,"object-"+i);
to.add(o);
}
this.setTestObjects(to);
}
public void testAjaxListener(int id){
System.out.println("testAjaxListener("+id+")");
this.setSelectedId(id);
}
//+getters/setters
}public class TestObject {
private int id;
private String name;
public TestObject(int id, String name){
this.setId(id);
this.setName(name);
}
//+getters/setters
}<?xml version="1.0" encoding="UTF-8"?>
<!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://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head></h:head>
<h:body>
<h:form id="testForm">
<h:panelGroup rendered="#{param['view'] eq 'test'}">
DataTable
<h:dataTable var="o" value="#{testBean.testObjects}">
<h:column>
<h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}">
<f:ajax
render=":testForm:outputTest"
/>
</h:commandLink>
</h:column>
</h:dataTable>
output
<h:outputText id="outputTest" value="#{testBean.selectedId}" />
</h:panelGroup>
</h:form>
</h:body>
</html><f:metadata>
<f:viewParam name="view" value="#{testBean.view}" />
</f:metadata>
...
<h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...>
<f:param name="view" value="#{param.view}" />
...
</h:commandLink><o:form includeRequestParams="true">
...
</o:form>
问题是,actionListener 不会触发(我正在使用 System.out.print 进行检查,如您所见)。当我从 panelGroup 中删除条件渲染时它工作正常,所以我认为这是问题所在 - 但我该如何解决?
我已经阅读了这些主题:
h:commandLink / h:commandButton 未被调用,
f:ajax 在有条件呈现的自定义标记内 - 未调用支持 bean 方法
还有更多,但它并没有解决我的问题:(
请帮忙
这是因为 #{param['view'] eq 'test'} 在 JSF 忙于处理 ajax 提交时没有评估 true。然后它还会再次查询 rendered、disabled 和 readonly 属性,以防止被黑客入侵的请求。 JSF 即不包括 <h:form> 生成的 <form action> URL 中的请求参数。这与未调用或未更新输入值的 commandButton/commandLink/ajax 操作/侦听器方法的第 5 点相匹配。
有几种方法可以解决这个问题。
通过 <f:viewParam>.
将其设置为视图范围 bean 的属性
@ManagedBean @ViewScoped public class TestBean implements Serializable { private static final long serialVersionUID = -2329929006490721388L; private List<TestObject> testObjects; private int selectedId; public TestBean(){ List<TestObject> to = new ArrayList<TestObject>(); for(int i=0; i<10; i++){ TestObject o = new TestObject(i,"object-"+i); to.add(o); } this.setTestObjects(to); } public void testAjaxListener(int id){ System.out.println("testAjaxListener("+id+")"); this.setSelectedId(id); } //+getters/setters }public class TestObject { private int id; private String name; public TestObject(int id, String name){ this.setId(id); this.setName(name); } //+getters/setters }<?xml version="1.0" encoding="UTF-8"?> <!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://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head></h:head> <h:body> <h:form id="testForm"> <h:panelGroup rendered="#{param['view'] eq 'test'}"> DataTable <h:dataTable var="o" value="#{testBean.testObjects}"> <h:column> <h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}"> <f:ajax render=":testForm:outputTest" /> </h:commandLink> </h:column> </h:dataTable> output <h:outputText id="outputTest" value="#{testBean.selectedId}" /> </h:panelGroup> </h:form> </h:body> </html><f:metadata> <f:viewParam name="view" value="#{testBean.view}" /> </f:metadata> ... <h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...> <f:param name="view" value="#{param.view}" /> ... </h:commandLink><o:form includeRequestParams="true"> ... </o:form>
通过<f:param>手动保留参数(你需要把它放在每个提交动作中!):
@ManagedBean @ViewScoped public class TestBean implements Serializable { private static final long serialVersionUID = -2329929006490721388L; private List<TestObject> testObjects; private int selectedId; public TestBean(){ List<TestObject> to = new ArrayList<TestObject>(); for(int i=0; i<10; i++){ TestObject o = new TestObject(i,"object-"+i); to.add(o); } this.setTestObjects(to); } public void testAjaxListener(int id){ System.out.println("testAjaxListener("+id+")"); this.setSelectedId(id); } //+getters/setters }public class TestObject { private int id; private String name; public TestObject(int id, String name){ this.setId(id); this.setName(name); } //+getters/setters }<?xml version="1.0" encoding="UTF-8"?> <!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://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head></h:head> <h:body> <h:form id="testForm"> <h:panelGroup rendered="#{param['view'] eq 'test'}"> DataTable <h:dataTable var="o" value="#{testBean.testObjects}"> <h:column> <h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}"> <f:ajax render=":testForm:outputTest" /> </h:commandLink> </h:column> </h:dataTable> output <h:outputText id="outputTest" value="#{testBean.selectedId}" /> </h:panelGroup> </h:form> </h:body> </html><f:metadata> <f:viewParam name="view" value="#{testBean.view}" /> </f:metadata> ... <h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...> <f:param name="view" value="#{param.view}" /> ... </h:commandLink><o:form includeRequestParams="true"> ... </o:form>
将 <h:form> 替换为 OmniFaces <o:form>,它能够告诉 JSF 它必须将表单提交到包含请求参数的 URL:
@ManagedBean @ViewScoped public class TestBean implements Serializable { private static final long serialVersionUID = -2329929006490721388L; private List<TestObject> testObjects; private int selectedId; public TestBean(){ List<TestObject> to = new ArrayList<TestObject>(); for(int i=0; i<10; i++){ TestObject o = new TestObject(i,"object-"+i); to.add(o); } this.setTestObjects(to); } public void testAjaxListener(int id){ System.out.println("testAjaxListener("+id+")"); this.setSelectedId(id); } //+getters/setters }public class TestObject { private int id; private String name; public TestObject(int id, String name){ this.setId(id); this.setName(name); } //+getters/setters }<?xml version="1.0" encoding="UTF-8"?> <!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://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head></h:head> <h:body> <h:form id="testForm"> <h:panelGroup rendered="#{param['view'] eq 'test'}"> DataTable <h:dataTable var="o" value="#{testBean.testObjects}"> <h:column> <h:commandLink value="#{o.name}" actionListener="#{testBean.testAjaxListener(o.id)}"> <f:ajax render=":testForm:outputTest" /> </h:commandLink> </h:column> </h:dataTable> output <h:outputText id="outputTest" value="#{testBean.selectedId}" /> </h:panelGroup> </h:form> </h:body> </html><f:metadata> <f:viewParam name="view" value="#{testBean.view}" /> </f:metadata> ... <h:panelGroup rendered="#{testBean.view eq 'test'}"><h:commandLink ...> <f:param name="view" value="#{param.view}" /> ... </h:commandLink><o:form includeRequestParams="true"> ... </o:form>
- 在 JSF 表单提交上保留 GET 请求查询字符串参数
也可以看看: