JSF form validation skip specific validator for second submit when user wants to override - validation

I am validating a user entered account number using two validators, one for basic standard format, and the other that validates the account number against values stored in a database. The database of valid account numbers may not always be up to date so I want to allow the user to override and submit their entered account number but only after the database validation has failed. I always want to validate its standard format 8 characters with no spaces.
<h:form id="formId">
<p:panelGrid>
<p:row>
<p:column>
<p:outputLabel value="Account : " for="acct" />
</p:column>
<p:column>
<p:selectOneMenu id="acct" value="#{bean.acct.acctNum}" effect="fold" editable="true" validator="acctLengthAndSpaceValidator" required="true" requiredMessage="Required">
<f:selectItems value="#{bean.mySavedAccounts}" var="acct"
itemLabel="#{acct.acctNumber} itemValue="#{acct.acctNumber}" />
<o:validator validatorId="accountDatabaseValidator" disabled="#{bean.skipDbValidation}" />
</p:selectOneMenu>
</p:column>
<p:column>
<p:messages for="acct" showDetail="true" skipDetailIfEqualsSummary="true" />
</p:column>
</p:row>
</p:panelGrid>
<br />
<p:selectBooleanCheckbox rendered="#{facesContext.validationFailed}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" listener="#{bean.testListener()}" />
</p:selectBooleanCheckbox>
<p:commandButton value="Submit" action="#{bean.submit()}" update="formId"/>
</h:form>
The checkbox does appear after the form is initially submitted and has any validation failure (I will figure out how to isolate to just the failed accountDatabaseValidator). But then when I select the checkbox, and submit again, both validators are still fired. I added the ajax listener to debug, and it isn't firing and the boolean value skipDbValidation is still false.
Perhaps my approach is not correct in achieving my concrete goal of validating against the database but then giving the user the option of skipping the db validation after initial failure.
EDIT
if i remove rendered="#{facesContext.validationFailed}" from the checkbox and have it visible all the time, the boolean skipDbValidation will get set to true if the checkbox is checked and then on subsequent submit, the skipDbValidation is ignored as expected. But I do not want the checkbox allowing the user to bypass visible at first. Only after validation fails.

The technical explanation that this doesn't work is that the rendered attribute is re-evaluated during processing the form submit. At this point the faces context is not validationFailed anymore (it was only validationFailed during the previous request) and thus the component is not rendered anymore and then the component's submitted value won't be applied. This matches #6 of commandButton/commandLink/ajax action/listener method not invoked or input value not set/updated.
Your work around by rendering it client-side rather than server-side is acceptable. But I gather that you wanted to show it only when the specific validator has been invoked. There are at least 2 ways to achieve this:
Check UIInput#isValid() of the input component of interest. You can achieve that by binding the component to the view (not to the bean!) via component's binding attribute so you can reference it elsewhere in the same view.
<p:selectOneMenu binding="#{acct}" ...>
...
</p:selectOneMenu>
...
<p:selectBooleanCheckbox styleClass="#{acct.valid ? 'ui-helper-hidden' : ''}" ...>
...
</p:selectBooleanCheckbox>
Note that I took the opportunity to reuse the PrimeFaces-provided style class.
Or, make the validator a view scoped bean and reference it via <o:validator binding> instead.
#Named
#ViewScoped
public class AccountDatabaseValidator implements Validator, Serializable {
private boolean validationFailed;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
// ...
validationFailed = !isValid(value);
if (validationFailed) {
throw new ValidatorException(createError("Invalid value"));
}
}
public boolean isValidationFailed() {
return validationFailed;
}
}
<p:selectOneMenu ...>
<o:validator binding="#{accountDatabaseValidator}" ... />
</p:selectOneMenu>
...
<p:selectBooleanCheckbox rendered="#{accountDatabaseValidator.validationFailed}" ...>
...
</p:selectBooleanCheckbox>

My work around to get the checkbox to programmatically display and so the checkbox would function was to hide and display using CSS instead of the render attribute.
style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}"
<p:selectBooleanCheckbox style="#{facesContext.validationFailed ? 'Display: inline' : 'Display: none;'}" value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>
But I still can't figure out how to display the checkbox for a specific validation failure.
I will post another question for that
EDIT
Here is how I ended up displaying the checkbox only after the Invalid Account validation failure.
<p:selectBooleanCheckbox style="#{facesContext.messageList.stream()
.anyMatch(v -> v.summary == 'Invalid Account') or
bean.skipDbValidation ? 'Display: inline' : 'Display: none;'}"
value="#{bean.skipDbValidation}" itemLabel="I know this account is really valid, please skip validation and let me submit!">
<p:ajax update="#this" />
</p:selectBooleanCheckbox>

Related

Model value of first input component is null in validator of second component

I need to get the value of a <p: selectOneMenu /> for use in a validator via <f:attribute>:
<p:selectOneMenu id="tDocument" value="#{usuarioController.persona.tipoDocumento}">
<f:selectItem itemLabel="#{msg.selectOne}" itemValue=""/>
<f:selectItems value="#{tipeListController.tipoIdentificacion}" var="_tDocument" itemValue="#{_tDocument}"/>
</p:selectOneMenu>
<p:inputText id="doc" value="#{usuarioController.persona.num_documento}" required="true" validator="ciRucValidator">
<f:attribute id="idenType" name="identificationType" value="#{usuarioController.persona.tipoDocumento}" />
</p:inputText>
But when trying to get it in the validator as below I get null:
TipoIdentificacion identificationType = (TipoIdentificacion) component.getAttributes().get("identificationType");
How is this caused and how can I solve it?
The model value is set during 4th phase "Update Model Values". However, validators run during 3rd phase "Process Validators". That is thus one phase earlier. It should be obvious that the updated model value of the other component is not available at that moment.
The canonical approach is to just pass the component and then extract the value directly from it via UIInput#getValue() or UIInput#getSubmittedValue() depending on the order of the components.
<p:selectOneMenu binding="#{tDocument}" ...>
...
</p:selectOneMenu>
<p:inputText ... validator="ciRucValidator">
<f:attribute name="tDocument" value="#{tDocument}" />
</p:inputText>
Note that I removed <f:attribute id>, this doesn't exist, and also note that binding example is as-is; very importantingly without a bean property.
You can grab it in the validator as below:
UIInput tDocument = (UIInput) component.getAttributes().get("tDocument");
TipoIdentificacion identificationType = (TipoIdentificacion) tDocument.getValue();
// ...
This also gives you the opportunity to invalidate the other component via setValid(false) if necessary.
See also:
How to get the value of another component in a custom validator?

Skip validation conditionally, when empty f:selectItem in p/h:selectOneMenu is selected

Parents :
<p:selectOneMenu id="parentList"
value="#{bean.selectedParent}"
converter="#{parentConverter}"
required="true">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="parent"
value="#{bean.parentList}"
itemLabel="#{parent.parentName}"
itemValue="#{parent}"/>
<p:ajax update="childrenList" listener="#{bean.setchildren}"/>
</p:selectOneMenu>
Children :
<p:selectOneMenu id="childrenList"
value="#{bean.selectedchild}"
converter="#{childConverter}"
required="true">
<f:selectItem itemLabel="Select" itemValue="#{null}"/>
<f:selectItems var="child"
value="#{bean.childrenList}"
itemLabel="#{child.childName}"
itemValue="#{child}"/>
</p:selectOneMenu>
The managed bean :
#Named
#ViewScoped
public class Bean implements Serializable {
#Inject
private Service service;
private Parent selectedParent;
private Child selectedChild;
private List<Parent> parentList;
private List<Child> childrenList;
private static final long serialVersionUID = 1L;
public Bean() {}
#PostConstruct
private void init() {
parentList = service.getParentList();
// Not necessary unless selectedParent is already available in edit mode.
if(selectedParent != null) {
childrenList = service.getChildrenListByParent(selectedParent);
}
}
public void setChildren() {
if(selectedParent != null) {
childrenList = service.getChildrenListByParent(selectedParent);
} else {
childrenList = null;
}
}
// Getters and setters.
}
The children list is to be populated based on their parent i.e. the children list should only contain children associated with a particular parent.
When the first parent in the parent list is selected, the children list should be reset to empty i.e. children should not be visible without their parent.
Since the parent list has a required field validator, it causes validation. When the first item in the parent list is selected, the children list will be prevented from being updated because of required="true". There is technically nothing wrong but existence of children without their parent may give end-users a bad experience.
What should happen is, when the first item in the parent list is selected, it should not cause validation i.e. the validation is to be skipped conditionally.
One way of doing this is to check either the selectedChild or childrenList itself is null/empty. Such as,
required="#{empty selectedChild or empty childrenList}"
But this does not appear to be a canonical way of skipping validation conditionally in such cases.
Does there exist a better way of skipping validation, when the first item in the parent list is selected so that the children list can be emptied as well along with the parent list (The validation should cause in all other cases. For example, when the form itself is submitted either synchronously or asynchronously)?
Basically, you want action-dependent validation. I.e. skip validation when that specific <p:ajax> action is invoked, and not on other actions.
This is unfortunately indeed not trivial to declare in the view. There are several tricks/workarounds for that. Most used one is to just check if the particular action is (not) invoked.
E.g. check if the desired save button is invoked by determining the presence of its client ID in the HTTP request parameter map as available by implicit EL object #{param}:
<h:form>
<p:selectOneMenu ... required="#{not empty param[save.clientId]}">
...
<p:ajax ... />
</p:selectOneMenu>
<p:selectOneMenu ... required="true">
...
</p:selectOneMenu>
<p:commandButton binding="#{save}" ... />
</h:form>
Or check if component's own <p:ajax> is not invoked by determining if component's own client ID does not equal the HTTP request parameter with predefined name javax.faces.source representing the source of the ajax request (the #{component} below is an implicit EL variable representing the current UIComponent):
<h:form>
<p:selectOneMenu ... required="#{param['javax.faces.source'] ne component.clientId}">
...
<p:ajax ... />
</p:selectOneMenu>
<p:selectOneMenu ... required="true">
...
</p:selectOneMenu>
<p:commandButton ... />
</h:form>
Or check if the parent form is submitted by UIForm#isSubmitted(), which would only evaluate true when a "full form submit" is used as in process="#form" (the <p:ajax process> defaults to #this which wouldn't trigger a "full form submit", and the <p:commandButton process> defaults to #form, which would thus trigger a "full form submit"):
<h:form binding="#{form}">
<p:selectOneMenu ... required="#{form.submitted}">
...
<p:ajax ... />
</p:selectOneMenu>
<p:selectOneMenu ... required="true">
...
</p:selectOneMenu>
<p:commandButton ... />
</h:form>
Or without binding the form by referencing the form via UIComponent#getNamingContainer() (if you know the position in the component tree; if the form is e.g. 2 naming container parents back, then use #{component.namingContainer.parent.namingContainer.submitted}):
<h:form>
<p:selectOneMenu ... required="#{component.namingContainer.submitted}">
...
<p:ajax ... />
</p:selectOneMenu>
<p:selectOneMenu ... required="true">
...
</p:selectOneMenu>
<p:commandButton ... />
</h:form>
Take your pick. The first solution has been offered several times before as it's most easy to understand and tweak by starters.
See also:
How to let validation depend on the pressed button?
Primefaces dependent selectOneMenu and required="true"
How to disable the required tag on a selectOneMenu when p:ajax event=change?

Submit empty value to bean anyway on input with required="true"

I've the below input with required="true":
<p:inputText value="#{bean.value}" required="true">
<p:ajax event="change" listener="#{bean.change()}" />
</p:inputText>
When user changes the value, the listener is fired and I can access the changed value. When user empties the field, the listener is not fired and the empty value is not updating in my bean. I gather that this is caused by requried="true". I would like to update my bean with empty value and fire the listener anyway when the user empties the field. How can I achieve this?
You can just use expression language (EL) in the required attribute too. You can then just check if the main submit button of the form has been pressed. Imagine that your form has a "Save" button like below,
<p:inputText ... required="true">
<p:ajax ... />
</p:inputText>
...
<p:commandButton ... action="#{bean.save}" />
Then you can let the required attribute evaluate true only if the button is invoked. You can achieve that by referencing the component via binding and checking if its client ID is present in the HTTP request parameter map:
<p:inputText ... required="#{not empty param[save.clientId]}">
<p:ajax ... />
</p:inputText>
...
<p:commandButton binding="#{save}" ... action="#{bean.save}" />
Note that #{save} is as-is and may absolutely not be bound to a backing bean, and that the variable name must be unique in the current view and EL scope.
See also:
How to let validation depend on the pressed button?
The issue is that if the user clears the required input field then 'required' validator throws an exception and bean setter will not be called. When the form is reloaded then cleared value will show up again from the bean. Here is my workaround:
public String getSomething() {
return isFormValueEmpty("form:something") ? null : this.something;
}
private Boolean isFormValueEmpty(String formFieldName) {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String formValue = ec.getRequestParameterMap().get(formFieldName);
logger.debug("Check if form value is empty: [{}] [{}]", formFieldName, formValue);
return StringUtils.isEmpty(formValue);
}

selectOneMenu ajax events

I am using an editable primefaces selectOneMenu to display some values. If the user selects an item from the List a textarea should be updated. However, if the user types something in the selectOneMenu, the textarea should not be updated.
I thought I could work this with ajax event out. However, I don't know which event I can use here. I only know the valueChange event. Are there any other events, like onSelect or onKeyUp?
Here is my code:
<p:selectOneMenu id="betreff" style="width: 470px !important;"
editable="true" value="#{post.aktNachricht.subject}">
<p:ajax event="valueChange" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<f:selectItems value="#{post.subjectList}" />
</p:selectOneMenu>
<p:inputTextarea style="width:550px;" rows="15" id="msgtext"
value="#{post.aktNachricht.text}" />
The PrimeFaces ajax events sometimes are very poorly documented, so in most cases you must go to the source code and check yourself.
p:selectOneMenu supports change event:
<p:selectOneMenu ..>
<p:ajax event="change" update="msgtext"
listener="#{post.subjectSelectionChanged}" />
<!--...-->
</p:selectOneMenu>
which triggers listener with AjaxBehaviorEvent as argument in signature:
public void subjectSelectionChanged(final AjaxBehaviorEvent event) {...}
I'd rather use more convenient itemSelect event. With this event you can use org.primefaces.event.SelectEvent objects in your listener.
<p:selectOneMenu ...>
<p:ajax event="itemSelect"
update="messages"
listener="#{beanMB.onItemSelectedListener}"/>
</p:selectOneMenu>
With such listener:
public void onItemSelectedListener(SelectEvent event){
MyItem selectedItem = (MyItem) event.getObject();
//do something with selected value
}
Be carefull that the page does not contain any empty component which has "required" attribute as "true" before your selectOneMenu component running.
If you use a component such as
<p:inputText label="Nm:" id="id_name" value="#{ myHelper.name}" required="true"/>
then,
<p:selectOneMenu .....></p:selectOneMenu>
and forget to fill the required component, ajax listener of selectoneMenu cannot be executed.
You could check whether the value of your selectOneMenu component belongs to the list of subjects.
Namely:
public void subjectSelectionChanged() {
// Cancel if subject is manually written
if (!subjectList.contains(aktNachricht.subject)) { return; }
// Write your code here in case the user selected (or wrote) an item of the list
// ....
}
Supposedly subjectList is a collection type, like ArrayList. Of course here your code will run in case the user writes an item of your selectOneMenu list.

Updating a checkbox from a radio button

I am trying to add ajax behavior to selectoneradio with this code:
xhtml:
<h:selectOneRadio id="metalA" converter="metalConverter" value="#{backingBean.metal.metalCode">
<f:selectItems value="#{backingBean.metalCodeRadio}" />
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalA" render="metalTypeMenuA"/>
</h:selectOneRadio>
<p:outputPanel id="panelA">
<h:selectOneMenu id="metalTypeMenuA" converter="metalConverter" value="#{backingBean.order.metal}" rendered="#{teklifIslemleriBean.selectedITip == 1}">
<f:selectItems value="#{backingBean.metalDetailsMenu}" />
</h:selectOneMenu>
</p:outputPanel>
backing bean:
MetalCode selectedMK = null;
public void updateMenu(AjaxBehaviorEvent event) {
System.out.println("Entered to updateMenu method");
if (metal.getMetalKod()!= null) {
electedMK = aMetal.getMetalCode();
}
if (selectedMK != null) {
// metalTypeMenuA Combobox
List<Metal> metalList = aService.getAccToMetalCode(null, selectedMK);
System.out.println("MetalList:" + metalList.size());
metalTypeMenuA.clear();
for (Metal m : metalList) {
metalTypeMenuA.add(new SelectItem(m, "No:" + m.getMetalNo() + " ,Weight: " + m.getWeight();
}
}
}
However it does not even enter to the updateMenu method. instead of click I tried select, change, etc. I also tried to put a wrapper panel and update it instead of checkbox, still no good. What is wrong with above code? Is updating a checkbox with a change in radiobutton doable? Thanks in advance.
JSF 2.0 Primefaces 2.2.1
EDIT:
I added following
<h:message for="metalA" id="messaged"/>
<f:ajax listener="#{backingBean.updateMenu}" event="click" execute="metalKoduA" execute="metalA" render="messaged orderPG2"/>
orderPG2 is a wrapper around checkbox. But still I can get any error message in h:message or any ajax behavior is happening.
The render attribute of <f:ajax> should not point to a component which is by itself conditionally server-side rendered by rendered attribtue. Let it point to the closest parent which is always rendered instead.
<f:ajax listener="#{backingBean.updateMenu}" render="panelA" />
(note that I removed event="click" and execute="metalA" as those are the defaults already)
If that still doesn't work, then you'd need to read the server logs for any missing faces messages. Big change that you'll see a Validation Error: "Value is not valid" or perhaps a conversion error. To prevent those messages from being missed during ajax rendering, ensure that you're using <h:message> and/or <h:messages> the right way and that you also include them in the render of the <f:ajax>.
<h:selectOneRadio id="metalA" ...>
...
<f:ajax ... render="metalAmessage panelA" />
</h:selectOneRadio>
<h:message id="metalAmessage" for="metalA" />

Resources