I'm having problem with h:form
, which contains composite
, which in turn contains p:dialog appendTo="@(body)
with h:form
. When p:selectOneRadio
is selected, an ajax is fired which somehow destroyes backing bean (@PostConstruct
method is invoked).
Someone had similar problem here (without solution provided).
Bean
@ManagedBean
@javax.faces.bean.ViewScoped
public class TestBean implements Serializable {
private String radioValue;
@PostConstruct
private void onPostConstruct() { /* ... */ }
public void init() { /* ... */ }
public void onCommandButtonAction() { /* ... */ }
public void onRadioChange() { /* ... */ }
public String getRadioValue() { return radioValue; }
public void setRadioValue(String radioValue) { this.radioValue = radioValue; }
}
View
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:custom="http://java.sun.com/jsf/composite/components">
<h:head>
</h:head>
<h:body>
<f:event type="preRenderComponent" listener="#{testBean.init}"/>
<h:form id="form">
<custom:radioComponent id="radioComponent"/>
</h:form>
</h:body>
</html>
Composite (radioComponent)
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:dialog="http://java.sun.com/jsf/composite/components/dialogs">
<composite:interface>
</composite:interface>
<composite:implementation>
<p:selectOneRadio
id="selectOneRadio"
value="#{testBean.radioValue}">
<f:selectItem itemLabel="a" itemValue="a" />
<f:selectItem itemLabel="b" itemValue="b" />
<p:ajax event="change"
listener="#{testBean.onRadioChange}"
update="text"/>
</p:selectOneRadio>
<h:outputText id="text" value="#{testBean.radioValue}"/>
<!-- if not present, @PostConstruct is NOT invoked -->
<dialog:formDlg id="formDlg"/>
</composite:implementation>
</ui:composition>
Dialog
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
</composite:interface>
<composite:implementation>
<!--
if appendTo="@(body)" is ommited,
radio button does not destroy bean
i.e. @PostConstruct is NOT invoked
-->
<p:dialog
id="formDlg"
widgetVar="formDlgWidget"
modal="true"
appendTo="@(body)">
<h:form id="dialogForm">
<!--
does not work if appendTo="@(body)" is present
if appendTo="@(body)" is ommited,
action is getting invoked...
-->
<p:commandButton
value="click"
action="#{testBean.onCommandButtonAction}"/>
</h:form>
</p:dialog>
</composite:implementation>
</ui:composition>
Note, dialog's form is rendered as input (I was expecting form tag):
EDIT
As Kukeltje point me out, this is a result of poor design. Forms nesting, even if nested form is moved out by appendTo="@(body)"
it's initially illegal construct as BalusC stated here. So the lesson is composites should never be wrapped by outer form as they may contain own forms.
WORKAROUND
If you want to avoid rewritting of whole page composition, you can use OmniFaces utility tag o:moveComponent to move "formDialog" out of the parent form.
<o:moveComponent
id="moveDialog"
for=":#{facesContext.viewRoot.clientId}"
destination="ADD_LAST">
<dialog:formDlg id="formDlg"/>
</o:moveComponent>
Note, for="{cc.attrs.parentFormClientId}"
resolves to an emtpy string, even if this attribute is provided.
` and in the composite of the selectOneRadio, surround the ` ` with a `... ` Not sure if it works though from composites. I use this construct all the time and it makes the `appendTo` superfluous btw.
– Kukeltje May 17 '19 at 11:58