PrimeFaces ajax update is not triggered when required input field becomes empty - validation

I am using JSF with PrimeFaces with fields like below.
<p:outputLabel for="vehicle" value="#{msg['vehicle.text']}" />
<p:autoComplete id="vehicle" value="#{newCtrlr.vehicleId.vehicle}" autocomplete="true" scrollHeight = "200" required = "true"
completeMethod="#{newCtrlr.vehicle_AutoComplete}" minQueryLength = "2">
<p:ajax event="itemSelect" listener = "#{newCtrlr.onVehicleChange}" update = "market" />
<p:ajax event="change" listener = "#{newCtrlr.onVehicleChange}" update = "market" />
</p:autoComplete>
<p:outputLabel for="market" value="#{msg['market.text']}" />
<p:inputText id="market" value="#{newCtrlr.market.marketName}"
readonly = "#{empty newCtrlr.vehicleId.vehicle ? 'true' :'false' }" />
In the above code, if the vehicle value is entered, the market should be made editable. If the vehicle value is cleared, the market should become readonly. Now since the vehicle input is 'required', the ajax update is not triggered and so the market remains enabled. If I remove the 'required' on Vehicle, everything works fine. I would like to know if there is a way to make the inputText required as well as trigger the ajax update when the value is changed to empty/null in the required field.

First of all, this behavior is default JSF behavior: If the input in invalid, it is not written to the model.
So you have to change this behavior manually, for example with an Event-Listener.
Add the following method to your Backing-Bean:
public void setModelValueToNullIfValidationFailed(ComponentSystemEvent event) throws AbortProcessingException {
UIInput input = (UIInput) event.getComponent();
if (input.isValid()) {
return;
}
input.getValueExpression("value").setValue(FacesContext.getCurrentInstance().getELContext(), null);
}
This method checks if the validation of the component has failed. If so the model value is set to null.
Add f:event to your p:autoComplete:
<p:autoComplete ...>
...
<f:event type="postValidate" listener="#{newCtrlr.setModelValueToNullIfValidationFailed}" />
</p:autoComplete>

Related

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

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>

How to postpone a keyup ajax request until h:inputText length is reached

Is there a way to postpone a keyup ajax request until a h:inputText value has reached a defined length?
I would like to reach the following goal:
a textInput field has to be filled with a combined date and time value. The expected format is: ddMMHHmm
Once the value reaches the length of 8 characters a new event object has to be added to an data list and should be displayed for confirmation immediately.
To confirm to add the new event the user simply presses enter inside this textInput field.
I don't know if there are different capabilities than using the ajax keyUp event to validate the input wihtout any further user interaction?
Here you see an very shortened example of my idea:
#Named
#SessionScoped
public class EventController {
private Date selectedDate; // +getter/+setter
private MyEvent event;
private List<MyEvent> events; // ArrayList<MyEvent>(), +getter
#PostConstruct
private void init() {
// load current events from DAO
}
public void validateInput() {
event = new MyEvent(selectedDate);
events.add(event);
}
public void confirmEvent() {
eventDAO.addEvent(event);
}
And the view:
<h:inputText
value="#{eventController.selectedDate}"
converter="#{comfortableDateTimeInputConverter}"
id="inputDateTime">
<f:ajax
<!-- pseudo code on !!! -->
executeCondition="<lengthOfInputField equals 8>"
<!-- pseudo code off !!! -->
execute="inputDateTime"
render="eventData"
event="keyup"
listener="#{eventController.validateInput}"
/>
</h:inputText>
<h:commandButton ... actionListener="#{eventController.confirmEvent}" />
<h:panelGroup id="eventData">
<h:dataTable var="..." value="#{eventController.events}">
// display event properties
</h:dataTable>
</h:panelGroup>
The ComfortableDateTimeInputConverter extracts the date an time parts of the input string and returns an date object.
I am using
primefaces 5.2
mojarra 2.2.8
Edit 1
As suggested by BalusC I modified my h:inputText, but nothing seems to happen. This is my original code exept the controller name. I've added a logging message inside eventController.validateNewEvent, but it seems not to be executed. Did I miss something?
<h:inputText
readonly="#{empty eventController.selectedPerson}"
value="#{eventController.selectedDate}"
id="inputDateTime"
tabindex="3"
converter="#{comfortableDateTimeInputConverter}"
onkeyup="return value.length >= 8"
onfocus="this.select()">
<f:ajax
event="keyup"
execute="inputDateTime"
listener="#{eventController.validateNewEvent}"
render="selectedDate txtDate listEvents" />
</h:inputText>
Also I tried to render="#all" at the ajax element, but still nothing happens. If i use event="blur" and leave the input with TAB it works like a charme ...
Edit 2 (resolved)
Replaced
onkeyup="return value.length >= 8"
with
onkeyup="return this.value.length >= 8"
and it works. See answer of BalusC ...
Just return false from onkeyup as long as value.length hasn't reached the desired value.
E.g.
<h:inputText ... onkeyup="return this.value.length >= 8">
<f:ajax event="keyup" ... />
</h:inputText>

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);
}

Validate input as required only if certain command button is pressed

I have specific use case for JSF validation. For example I have an inputText field:
<p:inputText id="input" required="#{myBean.required}" value="#{myBean.value}" maxlength="20" disabled="#{myBean.disabled}">
<p:ajax event="blur" process="#this" update="name" listener="#{myBean.listener}"/>
</p:inputText>
Value of input is number (in some cases it can also be a string, because this is part of composite component, but problem is better described if we assume this is a number). This input is part of the form, at the end of form I have submit button:
<p:commandButton value="Save" actionListener="#{myBean.save}"/>
What are my requests:
When submit button is pressed all validation should be processed and this is OK, this works fine.
When blur event is fired on input field if field is not empty a number validation should be processed, and this is also OK. At the end I update field with id name with some value.
Now I have a problem. My third request is when input is empty validation on input should not be processed. This is special case in which I will clear field with id name. This is also case when i remove text which is already entered in input, remove focus from component (press TAB for example) and in that case AJAX request should also be processed and name input will also be cleared.
How I can disable validation of this input field in case when it is empty, and just for this ajax event?
Let the input's required attribute check if the save button is pressed or not (which can be identified by the presence of its client ID in the request parameter map).
<h:form>
<p:inputText ... required="#{not empty param[save.clientId] and myBean.required}" />
<p:commandButton binding="#{save}" ... />
</h:form>
(note: do not bind it to a bean property! the code is as-is)
This way it would only evaluate true when the save button is actually pressed.
Or, if you have problems with binding and/or don't have a problem hardcoding the button's client ID:
<h:form id="formId">
<p:inputText ... required="#{not empty param['formId:buttonId'] and myBean.required}" />
<p:commandButton id="buttonId" ... />
</h:form>
Just remove the required attribute as you accept the input if the input is empty. Then write a custom validator which accepts only empty input or numerical input.
<p:inputText id="input" value="#{myBean.value}" maxlength="20" disabled="#{myBean.disabled}" validator="customerNumericInputValidator"> <p:ajax event="blur" process="#this" update="name" listener="#{myBean.listener}"/> </p:inputText>
public class customerNumericInputValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent uIComponent,
Object object) throws ValidatorException {
String number = (String) object;
number = Strings.nullToEmpty(number).trim();
//if the request is a full request then number can not be empty
if(!FacesContext.getCurrentInstance().isPostback() && Strings.isNullOrEmpty(number))
{
FacesMessage message = new FacesMessage();
message.setSummary(Messages.getMessage("error empty value"));
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
if(!Strings.isNullOrEmpty(number))
{
if(!isNumber(number))
{
FacesMessage message = new FacesMessage();
message.setSummary(Messages.getMessage("error not numerical value"));
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
}

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