I have a page using dynamic forms where I am creating the component tree programatically (which is not up for debate in this question) Some of the input controls I need to render require an ajax handler.
The xhtml fragment (included by a <ui:include>
from another fragment) is :
<ui:composition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:panelGroup id="id_Group1" binding="#{questionaire.group1}" layout="block"/>
</ui:composition>
Based on other SO anwsers, I have the following bean code:
public HtmlPanelGroup getGroup1() {
// irrelevant code omitted
HtmlSelectOneRadio selectUI = new HtmlSelectOneRadio();
AjaxBehavior valueChangeAction = (AjaxBehavior)FacesUtils.getApplication().createBehavior(AjaxBehavior.BEHAVIOR_ID);
valueChangeAction.addAjaxBehaviorListener(new ProbeQuestionListener(currentQuestion, "probeDiv" + questionNumber));
selectUI.addClientBehavior("change", valueChangeAction);
valueChangeAction.setRender(Collections.singletonList("probeDiv" + questionNumber));
// further code to customise the control, create the panel group and probe div and wire everything together omitted
}
This renders correctly and I see:
<input type="radio" onchange="mojarra.ab(this,event,'change',0,'probeDiv2')" value="0" id="answer_1:0" name="answer_1">
However, clicking the radio button gives me a javascript console error: reference error: mojarra is not defined
Now, if I modify the xhtml to include a "normal" ajax control, e.g.
<ui:composition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:panelGroup id="id_Group1" binding="#{questionaire.group1}" layout="block"/>
<!-- include a hacky hidden ajax field to force inclusion of the ajax javascript -->
<h:panelGroup layout="block" id="hiddenAjaxDiv" style="display:none">
<h:inputText id="hiddenAjax">
<f:ajax execute="hiddenAjax" render="hiddenAjaxDiv" />
</h:inputText>
</h:panelGroup>
</ui:composition>
This works and firebug network monitor shows my ajax event from the radio button is posted to the app.
So, finally, my question:
How do I programatically force the inclusion of the ajax javascript library and dispense with the horrible hack I am currently using?
Note: I am not interested in any answer that starts "don't use dynamically generated components" - for several reasons, this is not an option.
Basically, you need this:
That script contains the
mojarra
definition among the standardjsf
namespace containing the JSF ajax scripts.You can explicitly declare it in the
<h:head>
of your master template, if necessary via<ui:define>
/<ui:include>
. It won't load duplicate copies of thejsf.js
file if already implicitly required by the view.You can even programmatically create it:
Also here, it won't load duplicate copies of the
jsf.js
file if already implicitly required by the view.Unrelated to the concrete problem, you should prefer
<f:event type="postAddToView">
overbinding
when you need to programmatically populate the component tree:with
This guarantees that the tree is populated at exactly the right moment, and keeps getters free of business logic, and avoids potential "duplicate component ID" trouble when
#{questionaire}
is in a broader scope than the request scope, and keeps the bean free ofUIComponent
properties which in turn avoids potential serialization trouble and memory leaking when the component is held as a property of a serializable bean.