JSF passthrough radio button selection resets on form validation errors - jsf

I have a JSF 2.2 form comprised of an input text field and a pair of radio buttons displayed inline. Given the known limitations on radio button groups in JSF 2.2, I'm using the technique outlined by BalusC in this blog post. We cannot upgrade to JSF 2.3, as this is a Weblogic application and we're currently locked on Weblogic 12.2 (JavaEE 7).
While this technique works fine when a valid form is submitted, the problem is that if an invalid form is submitted, then the user's radio button selection is lost, and it resets to the last valid value (or initial value).
Here's an example of how I'm defining the radio button pair, using a h:inputHidden element and linking its binding attribute with the name attributes of the individual radio buttons (for their group ID).
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:jsf="http://xmlns.jcp.org/jsf"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
<div class="form-group">
<h:outputLabel for="heightInput"
value="Height"
styleClass="col-xs-6" />
<div class="col-xs-6">
<h:inputText id="heightInput"
value="#{modelBean.height}"
required="true" />
</div>
<div class="col-xs-12">
<div class="radio-inline">
<h:outputLabel for="heightCentimeters">
<input type="radio"
jsf:id="heightCentimeters"
pt:name="#{hiddenHeightUnitSelection.clientId}"
value="CENTIMETERS"
pt:checked="#{modelBean.heightUnit eq 'CENTIMETERS' ? 'checked' : null}" />
Centimeters
</h:outputLabel>
</div>
<div class="radio-inline">
<h:outputLabel for="heightInches">
<input type="radio"
jsf:id="heightInches"
pt:name="#{hiddenHeightUnitSelection.clientId}"
value="INCHES"
pt:checked="#{modelBean.heightUnit eq 'INCHES' ? 'checked' : null}" />
Inches
</h:outputLabel>
</div>
<h:inputHidden id="heightUnitSelection"
binding="#{hiddenHeightUnitSelection}"
value="#{modelBean.heightUnit}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
</div>
</div>
</ui:composition>
How can I preserve the user's radio button selection in the event an invalid form is submitted? The model is never updated with their selection. Other form elements preserve their values across form submissions, even when validation errors exist. How can I have my radio button group behave similarly?

Indeed, the checked attribute is comparing the model value directly.
pt:checked="#{modelBean.heightUnit eq 'CENTIMETERS' ? 'checked' : null}"
The model value is updated during UPDATE_MODEL_VALUES phase, but it won't be executed when there's a validation error encountered during PROCESS_VALIDATIONS phase.
Basically, you want to check the submitted value instead of the model value. This is already covered by the logic behind UIInput#getValue(). In your specific case, you want to compare against the value of the <h:inputHidden> instead.
pt:checked="#{hiddenHeightUnitSelection.value eq 'CENTIMETERS' ? 'checked' : null}"
The blog article linked in your question has in the meanwhile been updated.

Related

Omnifaces TagAttribute Id cannot be used as id

I have the following Facelet Taglib:
<ui:composition
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:o="http://omnifaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<o:tagAttribute name="id"/>
<h:panelGroup id="#{id}" layout="block" styleClass="idTest">
#{id}
</h:panelGroup>
</ui:composition>
The taglib.xml looks like this:
<tag>
<tag-name>idTest</tag-name>
<source>resources/myProject/tags/idTest.xhtml</source>
</tag>
And the code, where it is used is simply:
<myProject:idTest/>
How can it be, that the following HTML is being rendered:
<div class="idTest">
j_ido489794984_4bf870cd
</div>
Why does my PanelGroup have no id? The id is generated like it is expected based on the documentation of o:tagAttribute, since the content of the div is rendered. But as id it does not work. Why?
This is indeed confusing.
The documentation literally says:
... it will autogenerate an unique ID in the form of j_ido[tagId] where [tagId] is the <o:tagAttribute> tag's own unique ID.
But the actual behavior is more like so:
... it will override any autogenerated ID into the form of j_ido[tagId] where [tagId] is the <o:tagAttribute> tag's own unique ID.
In other words, when JSF itself needs to render the id attribute of a HTML element, usually because it's required by some internal logic further down in the chain, such as <f:ajax> and friends, and there is no explicit ID specified on the tag like so <x:someTag id="fixedId" />, then JSF will by default autogenerate one in the form of j_id[autoIncrementInteger]. But this will go wrong on tagfiles because the autoIncrementInteger may get bumped further by one on each postback depending on the JSF impl and view state configuration used. The <o:tagAttribute> simply attempts to ensure this way that the autogenerated ID stays the same on each postback.
When you edit your test tagfile to add <f:ajax>,
<h:panelGroup id="#{id}" layout="block" styleClass="idTest">
<f:ajax event="click" />
</h:panelGroup>
then you'll see that the generated <div> has an id, because this is technically required by <f:ajax>.
<div id="j_ido-1512689859_13a7e7e3" class="idTest"
onclick="mojarra.ab(this,event,'click',0,0)">
</div>
Or when you swap <h:panelGroup> for e.g. a <h:form> or whatever component which always requires an ID in the client side,
<h:form id="#{id}" styleClass="idTest">
<ui:insert />
</h:form>
then you'll also see that it's generated.
<form id="j_ido-1512689859_13a7e7e3" name="j_ido-1512689859_13a7e7e3" method="post" action="/test.xhtml" class="idTest" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_ido-1512689859_13a7e7e3" value="j_ido-1512689859_13a7e7e3" />
...
</form>
In other words, the feature is working fine, but it was simply not used in your specific case because JSF didn't consider it necessary to generate it.
I've in the meanwhile updated the documentation.

Unable to find component for ID in view

In a JSF, Primefaces 6 project, I get the following warning:
Unable to find component for ID dateFrom_input in view
Here is the view:
<h:outputLabel for="dateFrom_input" />
<p:autoComplete id="dateFrom" ...></p:autoComplete>
p:autoComplete is replaced by:
<span id="searchForm:dateFrom">
<input id="searchForm:dateFrom_input" name="searchForm:dateFrom_input" type="text" autocomplete="off">
...
</span>
Basically, I have to set the label for component dateFrom_input to get the feature working (I mean clicking on label to jump in the field...). It works but the warning appear.
It looks I cannot make reference to something in a widget...
How two shut this warning up?
UPDATE
In a DOM point of view, what I do is correct (label for parameter set with the input ID). But in a JSF point of view, p:autocomplete field is not yet "compiled" as HTML and it cannot reach the input (which effectively take ID dateFrom_input once compiled)
Please try to use the following:
<p:outputLabel for="dateFrom" value="Click here! "/>
<p:autoComplete id="dateFrom" ...></p:autoComplete>

Unable to ajax update component with p:commandLink click

I have three options for user: two equipment selection and one option to say I don't want equipment. To select two equipment I used p:commandLink. To say I do not want equipment I used h:selectOneRadio.
If user do not want to buy equipment, user can select the option button 'I do not wish to have'. So at anytime, only one button can be selected within the three of them.
If I press h:selectOneRadio button, then other two buttons (p:commandLink) are updating properly to 'select'. But if I select an equipment (p:commandLink), then h:selectOneRadio button is not deselecting even though it's bean value is updated to false.
JSF page
<h:panelGroup id="pwpPanel">
<div class="row">
<ui:repeat value="#{reconBean.EQList}" var="equipment">
<div class="col-md-4">
<div class="btn-fibre-tab text-center">
<ui:fragment rendered="#{reconBean.pwpSelectedEquipmentID != equipment.id }">
<p:commandLink id="selectpwp" process="#form" update=":mainForm:pwpPanel"
styleClass="select_button" action="#{reconBean.updatePWPItem(equipment.id)}"
immediate="true">
Select
<f:ajax render=":mainForm:pwpPanel" />
</p:commandLink>
</ui:fragment>
<ui:fragment rendered="#{reconBean.pwpSelectedEquipmentID eq equipment.id }">
<p:commandLink id="selectedpwp" process="#form" update=":mainForm:pwpPanel"
styleClass="selected_button" action="#{reconBean.updatePWPItem(equipment.id)}"
immediate="true">
Selected
<f:ajax render=":mainForm:pwpPanel" />
</p:commandLink>
</ui:fragment>
</div>
</div>
</ui:repeat>
</div>
<div class="row">
<div class="col-sm-12 ">
<h:selectOneRadio value="#{reconBean.notPurchaseWithPurchase}" id="radioDoNotWant"
name="pwpEquipment">
<f:selectItem itemLabel="I do not wish to have" itemValue="true" />
<f:ajax event="click" execute="#this" listener="#{reconBean.resetPWPSelection}"
render="pwpPanel" />
</h:selectOneRadio>
</div>
</div>
</h:panelGroup>
Bean values
public void updatePWPItem(String itemID) {
pwpSelectedEquipmentID = itemID;
notPurchaseWithPurchase = false;
}
public void resetPWPSelection(AjaxBehaviorEvent event) {
pwpSelectedEquipmentID = null;
}
The problem is the immediate attribute set to true on the CommandLinks. It is essentially to understand the The Lifecycle of a JavaServer Faces Application. The impact of the immediate attribute differs in, on what kind of component it is applied. As stated in a good article:
In the standard JSF lifecycle, the action attribute on an Command component is evaluated in the Invoke Application phase
... if you set immediate="true" on it.
To be honest, i do not exacly know why this prevents your radio option from being unchecked (I am still learning all aspects of the JSF lifecylce), but i think it is because it remains unchanged in the component tree and gets rendered as it was even if the model is updated through your action (if someone may comment on this i will update accordingly).
The solution to your problem is to remove the immediate attribute from your CommandLinks or set it to false.
Some remarks:
PrimeFaces CommandLinks have build in AJAX capabilities which are enabled by default. There is no need to enclose an additional <f:ajax />. Only if you want to disable AJAX you have to set its ajax attribute to false. The render attribute of Ajax conforms to the update attribute of CommandLink.
You are using SelectOneRadio in a context it is not designed for. As stated on JSFToolbox:
This component is designed for situations where you want to display a mutually exclusive list of options to the user as a set of radio buttons.
Consider using another better suited component like CommandLink or CommandButton.

Preset checked state of h:selectOneRadio with passthrough elements

I am trying to implement the solution described in
<h:selectOneRadio> renders table element, how to avoid this?
<div class="form-group">
<h:outputLabel styleClass="control-label"
value="#{msgClient.legalPersonality} " />
Selected value : <h:outputText value="#{msgClient['legalPersonality.' += clientBean.clientDto.legalPersonality.label]}" />
<div>
<f:metadata>
<f:viewParam name="legalPersonality" value="#{clientBean.clientDto.legalPersonality}" />
</f:metadata>
<ui:repeat var="legalPersonality" value="#{clientBean.legalPersonalities}">
<label class="radio-inline">
<input type="radio" jsf:id="legal" pt:name="legalPersonality" value="#{legalPersonality}">
<f:ajax execute="#this" render="#form" />
</input>
<h:outputText value="#{msgClient['legalPersonality.' += legalPersonality.label]}" />
</label>
</ui:repeat>
</div>
</div>
Everything seems to work fine, the selected value is correctly updated with the f:ajax component (setter is called with the correct value) BUT radio buttons are never checked. I mean it is checked when I click on it but it goes unchecked immediatly after. Even at page load, with a default value in clientDto.legalPersonality, everything is unchecked.
How it looks like : http://oi58.tinypic.com/242788g.jpg
Any help will be greatly appreciated. Thank you.
Indeed, the example in the question you found was a bit too oversimplified. It was missing the checked attribute which is necessary in order to preset the current selection on page load and/or after ajax render as in your case. In HTML, the checked state of a radio button is achieved by setting the checked attribute.
You can use <f:passThroughAttribute> to set the checked attribute, whereby you make sure that you set #{null} in unchecked case, so that it wouldn't render the checked attribute at all. It's namely a minimized attribute; even if we used checked="false", HTML would still consider it checked.
<input type="radio" jsf:id="legal" pt:name="legalPersonality" value="#{legalPersonality}">
<f:passThroughAttribute name="checked" value="#{legalPersonality eq clientBean.clientDto.legalPersonality ? 'checked' : null}" />
<f:ajax execute="#this" render="#form" />
</input>
The example in the question you found has in the meanwhile been updated. Thanks!

Issue in attribute for on h:outputLabel inside ui:repeat

I have following piece of code, i want to render radioButtons and labels for them. Due to the layout renderized by selectOneRadio and selectItem i decided to use plain HTML for the input radioButtons.
<h:panelGrid columns="6">
<ui:repeat value="#{myBean.list}" var="item">
<input id="#{item.innerList[0].id}" type="radio" name="radioItem" value="#{item.innerList[0].id}" />
<h:outputLabel for="#{item.innerList[0].id}" value="#{item.label}" />
</ui:repeat>
</h:panelGrid>
When trying to assign a label for them using outputLabel it renders the attribute for in HTML something like:
j_idt94:0:theidiassigned
The prependId flag in the tag form containing this code is set to false and in the documentation there is no such flag in the attributes of the other containing tags of the outputLabel.
The ui:repeat component is a NamingContainer. It controls the client identifier of its children.
The for attribute on a h:outputLabel will generally look for a component with a matching component identifier or client identifier.
So, for this markup:
<foo:someNamingContainer id="x">
<h:inputText id="foo" />
<h:outputLabel for="foo" />
</foo:someNamingContainer>
...the label would emit the matching, namespaced identifier <label for="...:x:foo".
There is no matching JSF component with a matching id since you are using a straight XHTML <input> element.
Consider just using a XHTML <label> element instead.

Resources