#NotEmpty List validation - jsf

I have a JSF component which is mapped on a List in the corresponding bean.
As I want the mapped field to contain at least one value when the form is submitted, this field is annoted #NotEmpty.
Bean :
public class MyBean {
#NotEmpty(message="Validation failed")
private List<String> selectedOptions = new ArrayList<>();
... getter & setter
}
Each time the user select something on my component I want the selection to be immediatly mapped in the bean, so I add an ajax behaviour to the component (Here the component is a p:selectManyMenu but the issue seems to be present with any component mapped to a List) :
XHTML :
<h:form>
<p:outputLabel for="optionslist" value="Options :" />
<p:selectManyMenu id="optionslist" value="#{myBean.selectedOptions}" style="margin-bottom:50px">
<f:selectItem itemLabel="Option 1" itemValue="1" />
<f:selectItem itemLabel="Option 2" itemValue="2" />
<f:selectItem itemLabel="Option 3" itemValue="3" />
<p:ajax process="#this" update="result optionsListMessage" />
</p:selectManyMenu>
<p:commandButton value="Submit" update="result optionsListMessage" />
<p:message for="optionslist" id="optionsListMessage" />
<p:dataList id="result" value="#{myBean.selectedOptions}" var="option">
<h:outputText value="#{option}" />
</p:dataList>
</h:form>
My issue happens in the following situation :
The user selects one or several choices (Ctrl + click here).
He unselects every choices he just selected.
He submits the form.
We can see in the result dataList that the last unselected value is still in the bean.
My understanding of the situation is that when the user unselects the last value, the validation fails because of the #NotEmpty annotation on the field (as confirmed by the p:message validation failure message). As a consequence the setter is not called and the last unselected value remains in the bean.
How can I, in a proper way, allow the user to unselect every items without validation failures, and run the validator on this field only when the form is submitted ?

Related

Omit validation when making change on selectOneMenu with ajax

I have a selectOneMenu in a form with an ajax to update a separate component when it is triggered. This menu also uses validation. This first item in the menu is null and the validation makes sure that you can't submit the form if this item is null. However, I do want the user to be able to select the null item and that it set the value of that item in my backing bean to null. However the set is not happening when the user selects the null item because the validation failure occurs first and is throwing an exception preventing the value from being set to null in my backing bean.
Here is my code
<h:form>
...
<p:selectOneMenu value="#{myBean.container}">
<f:selectItem itemLabel="Select One" itemValue=#{null} />
<f:selectItems value="#{myBean.getContainerItems()}" />
<p:ajax event="change" update="containerNumSection" />
<f:validateRequired />
</p:selectOneMenu>
...
</h:form>
Is there any way that I can have the value in my backing bean set to null when the user selects the null item while keeping the validation?
I have found a somewhat dirty workaround using a custom validation method that is able to distinct cases where either the selectOneMany is changed or the submit button is pressed:
<h:form>
<p:selectOneMenu id="select" value="#{myBean.stringVal}"
validator="#{myBean.validateSelection}">
<f:selectItem itemLabel="nothing" itemValue="#{null}" />
<f:selectItem itemLabel="a" itemValue="a" />
<f:selectItem itemLabel="b" itemValue="b" />
<p:ajax update="#form" process="#this"/>
</p:selectOneMenu>
<p:commandButton value="submit" update="#form" process="#form">
<f:param name="selectionRequired" value="true"/>
</p:commandButton>
<h:message for="select" />
<h:outputText value="#{myBean.stringVal}" />
</h:form>
If the user presses the submit button, a request parameter named "selectionRequired" is set to "true". If this is the case, the validation method delegates to the javax.faces.validator.RequiredValidator.validate() method, else it simply does nothing.
MyBean.validateSelection:
public void validateSelection(FacesContext context, UIComponent component, Object value) {
String selectionRequired = FacesContext.getCurrentInstance().getExternalContext()
.getRequestParameterMap()
.get("selectionRequired");
if (Boolean.valueOf(selectionRequired)) {
new RequiredValidator().validate(context, component, value);
}
}
I had to enable javax.faces.VALIDATE_EMPTY_FIELDS in order for this to work, else the validateSelection method was not invoked at all when #{null} is selected.
web.xml:
<context-param>
<param-name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>
<param-value>true</param-value>
</context-param>

How to make selected h:selectOneRadio of h:dataTable remain selected on postback?

In normal circumstances like this:
<h:form>
<h:selectOneRadio value="#{bean.gender}">
<f:selectItem itemValue="Male" itemLabel="Male" />
<f:selectItem itemValue="Female" itemLabel="Female" />
<f:selectItem itemValue="Other" itemLabel="Other" />
</h:selectOneRadio>
<h:commandButton value="Submit" action="#{bean.action}" />
</h:form>
Selecting one radio button disselects the other & the radio button will be remain selected on the postback. (when the same view is rendered)
However, when we're dealing with an iterating component like <h:dataTable>, the selection is lost.
Consider the snippet:
<h:form id="hashMapFormId">
<b>HASH MAP:</b>
<h:dataTable value="#{playersBean.dataHashMap.entrySet()}" var="t" border="1">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio id="radiosId" onclick="deselectRadios(this.id);"
value="#{playersBean.select}">
<f:selectItem itemValue="null"/>
</h:selectOneRadio>
</h:column>
</h:dataTable>
<h:commandButton value="Show Hash Map Selection"
action="#{playersBean.showSelectedPlayer()}" />
</h:form>
With disselecting the other radio buttons when one radio button is selected being implemented by simple JavaScript-
function deselectRadios(id) {
var f = document.getElementById("hashMapFormId");
for (var i = 0; i < f.length; i++)
{
var e = f.elements[i];
var eid = e.id;
if (eid.indexOf("radiosId") !== -1) {
if (eid.indexOf(id) === -1) {
e.checked = false;
} else {
e.checked = true;
}
}
}
}
Fire the GET request:
Select a radio button:
Now press the submit button, response:
You see that the radio button gets dis selected on postback. How to solve this shortcoming?
I know it very well that this is due to this component attribute itemValue being null:
<f:selectItem itemValue="null"/>
This trick is a leftover from JSF 1.x / 2.0/2.1 when it wasn't possible to use a <h:selectOneRadio> for single row selection in a <h:dataTable>. This trick originated in my 10 year old blog article Using Datatables - Select row by radio button.
The root problem is, HTML radio buttons are grouped based on their name attribute, so the webbrowser knows which others to unselect when one is selected. But JSF generates by design a different one for each <h:dataTable> item, with the row index inlined and therefore they can't be grouped and hence the JavaScript based workaround.
Since JSF 2.2, with the new passthrough elements and attributes feature, it's however possible to force the name attribute to the value of your choice and capture the selected item via a helper <h:inputHidden>. This is fleshed out in another blog article of me, from previous year: Custom layout with h:selectOneRadio in JSF 2.2. The article uses <ui:repeat> as an example, this can be rewritten to <h:dataTable> as below.
<h:form>
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
value="#{item.id}" a:checked="#{item.id eq bean.selectedItemId ? 'checked' : null}" />
</h:column>
<h:column>#{item.id}</h:column>
<h:column>#{item.name}</h:column>
</h:dataTable>
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItemId}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
#Named
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private Long selectedItemId;
// ...
public void submit() {
System.out.println("Selected item ID: " + selectedItemId);
}
// ...
}
And yes, the selected radio button remains selected on postback this way. You can also pass whole entities, this only requires a converter on the <h:inputHidden>.

f:selectItems in a p:selectOneMenu are not triggering the setter for the given field

This is my current structure for my p:selectOneMenu:
<h:form id="groupSelectionForm">
<p:outputLabel value="Momentane Gruppe:" for="groupSelection" />
<p:selectOneMenu id="groupSelection" value="#{foodPlanManagementBean.selectedGroup}" style="width:150px">
<f:selectItem itemLabel="-" itemValue="#{null}"/>
<f:selectItems value="#{foodPlanManagementBean.getGroups()}" var="group" itemLabel="#{group.name}" itemValue="#{group}"/>
<p:ajax event="change"/>
</p:selectOneMenu>
</h:form>
This results in a checkbox containing a default value given by the single selectItem as well as a few generated options from the selectItems.
However, the setter for the given field "selectedGroup" is only triggering for the selectItem.
The selectItems do not seem to do anything when they are being clicked.
Any ideas?
try to define a listener in ajax component, ex:
<p:ajax id="seasonAjax" event="change" process="#this" listener="#{yourBean.yourMethod}" update="elementThatYouWantToUpdate" />
process = this to process selected element.
In selectItems don't use get method use directly list elements(put get/set in your bean) ex:
<f:selectItems value="#{yourBean.yourList}" var="" itemLabel="" itemValue="" />
If this doesn't work test if you need to use a converter, if selectedGroup is a complex object or pass directly identification of selectGroup( selectedGroup.id)
I hope it helps.

using JSF binding + validator in the same component display message twice

I have 2 components(select and inputText), in which values are dependent to each other. For example if "option 1" is selected then inputText must be numbers.
In my bean I have added attributes for 2 components for binding and a validation method, while in jsp i have added "validator" and "binding" attribute to select and "binding" to inputText.
I used binding to get the submitted value of both components for validation.
Is this the correct way? Is there an alternative to get the submitted value?
The result of doing this is duplicate message shown. If I remove binding attribute from select then it works as expected but I cannot fetch the selected value, rather is uses the cache value (bean value in session).
Thanks in advance.
aalmero
code:
<p:selectOneMenu
value="# {deploymentRequestViewBean.deploymentRequestDTO.deploymentRequest.requestLevel}"
id="requestLevel" required="true" label="requestLevel"
validator="#{deploymentRequestViewBean.validateRequestDate}">
<p:ajax listener="#{deploymentRequestViewBean.processRequestLevelValueChanged}"
binding="#{deploymentRequestViewBean.requestLevelSelectOne}"/>
<f:selectItem itemValue="" itemLabel="Select One" />
<f:selectItem itemValue="DEV" itemLabel="DEV" />
<f:selectItem itemValue="QUA" itemLabel="QUA" />
<f:selectItem itemValue="PRD" itemLabel="PRD" />
</p:selectOneMenu>
<p:calendar
value="#{deploymentRequestViewBean.deploymentRequestDTO.deploymentRequest.deployDate}"
id="deployDate" required="true" label="deployDate" showOn="button" pattern="yyyy- MM-dd" binding="#{deploymentRequestViewBean.requestDateInput}"/>
<p:spacer width="10" height="10" />
//for component-binding
private UISelectOne requestLevelSelectOne;
private UIInput requestDateInput;
//validation method
public void validateRequestDate(FacesContext facesContext,
UIComponent component, Object newValue){
//get the current value of select;
requestLevelSelectOne.getSubmittedValue();
//get the current vallue of input;
requestDateInput.getSubmittedValue()
if(not valid combination){
facesContext.addMessage(requestDateInput.getClientId(facesContext), new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", selectedLevel + " deployment request requires at least 2 days."));
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Deployment date must be at least 2 days."));
}
}
You can use a hack bypass by binding a hidden component value with the select component. In the "onchange" method of your <h:selectOneMenu> you can set the value of this hidden component and get the values in the server:
<h:form id="myForm">
<h:selectOneMenu id="cmbOptions"
onchange="document.getElementById('myForm:hidSelectOption').value=this.value">
<f:selectItem itemLabel="Option 1" itemValue="1" />
<f:selectItem itemLabel="Option 2" itemValue="2" />
<f:selectItem itemLabel="Option 3" itemValue="3" />
</h:selectOneMenu>
<h:inputHidden id="hidSelectOption" value="#{bean.selectedOption}" />
<h:commandButton value="Click me" action="#{bean.someAction}" />
</h:form>
The managed Bean
#RequestScope
#ManagedBean
public class Bean {
private String selectedOption;
//getters and setters...
public Bean() {
}
public void someAction() {
//showing the actual value of the hidden component...
//remember that you should use a logger, this is a basic example
System.out.println(selectedOption);
}
}

JSF f:selectItem in h:selectManyCheckbox not working in backing bean, but is displayed properly in h:dataTable

The problem occurs with this code:
<h:form>
<rich:panel>
<f:facet name="header">
<h:selectManyCheckbox title="Select which types of requests you want to see"
onchange="submit();" value="#{filterListener.chosenFilters}"
id="selectBoxContainer" >
<f:selectItem id="approvedByITS" itemLabel="Approved by ITS" itemValue="approvedByITS" />
<f:selectItem id="approvedByPO" itemLabel="Approved by Process Owner" itemValue="approvedByPO" />
<f:selectItem id="dob" itemLabel="Date" itemValue="dob" />
<f:selectItem id="externalAssignedTo" itemLabel="External assigned" itemValue="externalAssignedTo" />
<f:selectItem id="internalAssignedTo" itemLabel="Internal assigned" itemValue="internalAssignedTo" />
<f:selectItem id="ITSapprovedBy" itemLabel="ITS approved by" itemValue="ITSapprovedBy" />
<f:selectItem id="severity" itemLabel="Severity" itemValue="severity" />
<f:selectItem id="status" itemLabel="status" itemValue="status" />
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
</h:selectManyCheckbox>
</f:facet>
<h:dataTable value="#{filterListener.chosenFilters}" var="selects" >
<h:column>
<h:outputText value="#{selects}" />
</h:column>
</h:dataTable>
<br />
<h:messages />
</rich:panel>
</h:form>
As we can see I have the value="#{filterListener.chosenFilters}". The dataTable's value is also the same, so whenever I click one of the selectItem's the dataTable has an element added or removed from it (this is working). In my backing bean I have the following code:
public class requestFilterListener implements ValueChangeListener {
private List<String> chosenFilters;
public requestFilterListener() {
}
public void processValueChange(ValueChangeEvent event) {
System.out.println("processValueChange called");
if (chosenFilters != null) {
System.out.println(chosenFilters.size());
}
}
public List<String> getChosenFilters() {
return chosenFilters;
}
public void setChosenFilters(List<String> chosenFilters) {
this.chosenFilters = chosenFilters;
}
Everytime I click one of the checkboxes, a column is added/removed with the proper data, in my console I get the message "processValueChange called" as I output in the processValueChange method, but during this time chosenFilters is always null, and the if expression is never run. How come? This is a session bean, and I really dont understand why my list cant be used within the backing bean, but is used without a problem by my dataTable.
Thanks for your time in looking into this.
The problem is probably on this tag:
<f:valueChangeListener type="no.ngt.tech.rt2.listeners.requestFilterListener" />
You are instructing the f:valueChangeListener tag to create a new instance of requestFilterListener instead of binding to the one specified by the managed bean configuration. Use the binding attribute to bind to #{filterListener}.

Resources