I've hit a problem using Ajax within composite components (as opposed to Ajaxifying custom components using clientBehavior attribute).
I've hit the old problem of the target of an ajax request needing to be present in the DOM when the page is initially rendered.
The custom component has some internal ajax interactions, but is accessed from a div that is initially rendered=false, and ajax stops working, typically without any error messages in the server log.
This simple example demonstrates the problem. If the showPanel property is initially set true you can type in some text, hit the push link and see the text updated on the screen - everything ok. If showPanel is set to false, you can hit the Toggle button which displays the panel but the "push" link no longer operates.
Clearly it would be beneficial if this composite could act as a discrete component, fully enscapulated.
Does anyone know if there's a way round this? I've just spent ages writing an all singing all dancing component and the first time I try to use it it d开发者_如何学Coesn't work!
Thanks.
Mojarra 2.0.4 Glassfish 3.0.1 IE8/Chrome.
index.xhtml
<?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:sqcc="http://java.sun.com/jsf/composite/sqcc"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form prependId="false">
<h:panelGroup id="ajaxTarget">
<h:panelGroup rendered="#{indexBean.showPanel}">
<sqcc:testcomp value="#{indexBean.text}"/>
</h:panelGroup>
</h:panelGroup>
<h:commandButton value="Toggle" action="#{indexBean.togglePanel}">
<f:ajax render="ajaxTarget"/>
</h:commandButton>
</h:form>
</h:body>
</html>
resources/sqcc/testcomp.xhtml
<?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:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="value"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<div id="#{cc.clientId}">
<h:form prependId="false">
<h:inputText id="input1" value="#{cc.attrs.value}"/>
<h:outputText id="output1" value=" #{cc.attrs.value}"/><br/>
<h:commandLink value="push">
<f:ajax execute="input1" render="output1"/>
</h:commandLink>
</h:form>
</div>
</cc:implementation>
</html>
IndexBean.java
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
@ManagedBean
@ViewScoped
public class IndexBean implements Serializable {
public IndexBean() {
}
private boolean showPanel = true;
private String text;
public String togglePanel() {
showPanel = !showPanel;
return null;
}
public boolean isShowPanel() {
return showPanel;
}
public void setShowPanel(boolean showPanel) {
this.showPanel = showPanel;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
[end]
Shouldn't really be answering my own question, but I came back to this after a few weeks. The big error in my original post was that the Ajax target only needs to be rendered, not necessarily on first page load, as everyone knows. I must have been getting tired.
The behavior exhibited by my test application, whereby the "ajax within the composite component" would not work if an outer container was not initially rendered, was actually correct and reproducible, but only if the logic inside the composite component implementation is inside it's own form. Take out the h:form from the cc and everything is ok.
Brendan.
精彩评论