<f:validator disabled flag not working - ajax

I've created a custom validator for my project, it simply checks the select ones value and 'validates' the value is not '0'. We have a standard (I'm sure not uncommon) of manually setting the first value of our selectOneMenu compents to:
<f:selectItem itemValue="0"
itemLabel="-- Select One --"/>
Which works fine, but then makes the component always pass the required check. So this validator simply treats this value as if there was no selection made.
SelectOneMenu example:
<h:selectOneMenu id="eligibility"
value="#{reg.eligibility}"
required="#{reg.fieldsRequired}">
<f:selectItem itemValue="0"
itemLabel="-- Select One --"/>
<f:selectItems value="#{reg.eligibilityList}" />
<f:validator validatorId="selectOneValidator"
disabled="#{!reg.fieldsRequired}"/>
Custom Validator:
#FacesValidator("selectOneValidator")
public class SelectOneValidator implements Validator {
#Override
public void validate(FacesContext context, UIComponent uiComponent, Object o) throws ValidatorException {
String val = null;
if (uiComponent instanceof HtmlSelectOneMenu) {
HtmlSelectOneMenu oneMenu = (HtmlSelectOneMenu) uiComponent;
if (oneMenu.isRequired() && !oneMenu.isDisabled()) {
if (o instanceof String) {
val = (String) o;
} else if (o instanceof Number) {
val = String.valueOf(o);
}
if ("0".equals(val)) {
FacesMessage msg = new FacesMessage();
msg.setSummary("Please select a value from the list.");
msg.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(msg);
}
}
}
}
}
This has worked just fine thus far, however, the current use-case I'm running into trouble.
My page has multiple SelectOneMenus one of which toggles the required and disabled attributes via an ajax call for the page. I'm not having any issues with the required="#{reg.fieldsRequired}", however; the disabled="#{!reg.fieldsRequired}" attribute on my custom validator does not seem to make a difference. I'm just thinking out loud, but when the page first loads the #{reg.fieldsRequired} expression is false. If I then change the SelectOneMenu to set this boolean value to true, then press the submit button, the disabled attribute doesn't seem to have been set. I wondered if this is simply a ajax issue and that all of components simply needed to be re-renderd so I added the #form in my ajax call: <a4j:ajax render="#form" listener="#{reg.saveActionChanged}"/> in hopes that would fix the problem, but no difference in the behavior.
Environment:
JSF 2.0.3
Tomcat 6.0.14
ajax call being made with RichFaces 4.0
Any help is most appreciated!

Sorry, I can't reproduce your problem with JSF 2.1.3. The <f:validator disabled> works as expected.
However, as a completely different alternative, you can in this particular case just make use of the standard required validator. You only need to set the item value to #{null} instead of 0.
<f:selectItem itemValue="#{null}" itemLabel="-- Select One --"/>
This way you don't need the custom validator. The message can be set as requiredMessage attribute of the input component.
<h:selectOneMenu requiredMessage="Please select a value from the list.">

Related

How to enable/disable specific item in selectManyCheckbox on ajax keyUp

I need your help in enabling/disabling a specific item in the selectManyCheckbox component based on the ajax call keyup.
When the page loads, I am firing the below method to populate the selectManyCheckbox items in the form:
#PostConstruct
public void init() throws SQLException {
this.hrCertificatesList.add(new hrCertificate(("Employment"), "CE", false));
this.hrCertificatesList.add(new hrCertificate(("Loan"), "LC", false));
}
And here is the jsf code:
<p:inputText id="selectedEmployee" value="#{HRRequest.selectedEmployeeCode}">
<p:ajax event="keyup" update="employeeName" listener="#{HRRequest.getEmployeeName}" />
</p:inputText>
<h:outputText id="employeeName" value="#{HRRequest.selectedEmployeeName}" />
<p:selectManyCheckbox id="hrCertificates" value="#{HRRequest.selectedHRCertificates}">
<f:selectItems value="#{HRRequest.hrCertificatesList}" var="hrCertificate"
itemLabel="#{hrCertificate.hrCertificateName}"
itemValue="#{hrCertificate.hrCertificateCode}" itemDisabled="#{hrCertificate.hrBooleanCertificate}"/>
</p:selectManyCheckbox>
Once the page loads, all the checkboxes are enabled and when the user enters employeeCode in the inputText, an ajax will be fired to call a method to get the employeeName and to check whether has loan or not, if has loan, then the checkbox should be enabled, otherwise disabled.
To summarize my issue, what I want is that when the value of the variable temp equals to yes, then I need to disable the loan checkbox only and the other item Employment should remain enable, so how can I do this?
The bean code is:
public String getEmployeeName() throws SQLException {
if (temp.equals("Yes"))
{
//How to enable and disable the Loan checkbox only and to update the form view
RequestContext.getCurrentInstance().update(":HRform:hrCertificates");
}
So can you please help.
Just manipulate the model in such way that itemDisabled="#{hrCertificate.hrBooleanCertificate}" evaluates true instead of false so that the view knows what it must do.
One way might be:
this.hrCertificatesList.get(1).setHrBooleanCertificate(true);

issue with ajax in primefaces "selectOneMenu"

index.xhtml
<p:selectOneMenu id="d2" value="#{mainManageBean.areaSelected}" >
<f:selectItem itemValue="" itemLabel="Select one" />
<f:selectItems value="#{mainManageBean.areaList}" var="area"
itemValue="#{area.id}" itemLabel="#{area.name}"/>
<p:ajax event="valueChange" listener="#{mainManageBean.changeAreaSelect()}" update="hi" />
</p:selectOneMenu>
When i have value set like this "mainManageBean.areaSelected" where areaSelected is entity from database
private Area areaSelected;
the ajax event dont work,
but when i change it to something like this "mainManageBean.s1menu" where this "s1menu" is just a normal String ajax event work fine.
What is the reason of that and how to fix it?
Edit
this is my buged converter:
#FacesConverter
public class areaConverter implements Converter{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
// here i have problem value is id of entity and have no idea how to get this entity form this id
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
// works fine value = Area entity
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null || value.equals("")) {
return "";
} else {
return String.valueOf(((Area) value).getId());
}
}
}
Have problem with getAsObject, i have this value as id but cant get entity from it. Normaly i would use AreaFacade.find(id) but i cant use there #EJB to get it.
First of all, your <f:selectItem itemValue> is wrong. It should represent exactly the same type as <p:selectOneMenu value>, which is thus Area. Replace itemValue="#{area.id}" by itemValue="#{area}". You indeed need a Converter for this.
As to your problem with the converter,
Have problem with getAsObject, i have this value as id but cant get entity from it. Normaly i would use AreaFacade.find(id) but i cant use there #EJB to get it.
you have 2 options:
Make it a #ManagedBean #RequestScoped instead of #FacesConverter and reference it as converter="#{areaConverter}" instead of converter="areaConverter".
Install OmniFaces >= 1.6. It adds full transparent support for #EJB inside #FacesConverter without any additional configuration or annotations.
If you go the OmniFaces path anyway, then you could also just throw away your custom converter altogether and go for its builtin SelectItems(Index)Converter without the need to create any custom converter for itemValue="#{area}".
<p:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
See also:
CDI Injection into a FacesConverter
You are trying to set an Entity with value of ID, I assume Integer or String? The reason why ajax does not fire is because event="valueChange" does not occur. If you tried to submit this form without ajax you would get a sweet ClassCastException. As I mentioned in my comment and Makky in his answer, change itemValue to:
<p:selectOneMenu id="d2" value="#{mainManageBean.areaSelected}" >
<f:selectItem itemValue="#{null}" itemLabel="Select one" />
<f:selectItems value="#{mainManageBean.areaList}" var="area"
itemValue="#{area}" itemLabel="#{area.name}"/>
<p:ajax listener="#{mainManageBean.changeAreaSelect()}" update="hi" process="#this />
</p:selectOneMenu>
As per comment from Kuba
<f:selectItems value="#{mainManageBean.areaList}" var="area"
itemValue="#{area.id}" itemLabel="#{area.name}"/>
change itemValue as
<f:selectItems value="#{mainManageBean.areaList}" var="area"
itemValue="#{area}" itemLabel="#{area.name}"/>
Update:
The other thing it could be the converter. My suggestion is to use the SelectItemsConverter from Onmnifaces.
Omnifaces select item converter

How to disable default JSF validation and conversion of one input field

I want to disable the default JSF validation and conversion of one input field inputtext in order to be able to validate it using jQuery.
<p:column headerText="Quantité">
<p:inputText widgetVar="input_qte" styleClass="my_qte" value="#{arti.qte}">
<f:validateBean disabled="true"/>
</p:inputText> \
<h:outputText styleClass="my_qtemax" value="#{arti.qtemax}" />
<div class="my_validator" style="display : none;">Valeur Invalide</div>
</p:column>
The #{arti.qte} is bound to a Double property.
How can I achieve this?
There's already no validation on that component, as far as I see in the information provided so far. Perhaps you specifically meant the implicit conversion when you bind a non-String type as input component's value? No, you can't disable this. You can only workaround it by supplying a custom converter which doesn't throw an exception, but just returns null on failure.
E.g. by just extending the standard JSF DoubleConverter and totally suppressing the ConverterException on getAsObject():
#FacesConverter("lenientDoubleConverter")
public class LenientDoubleConverter extends DoubleConverter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
try {
return super.getAsObject(context, component, value);
} catch (ConverterException ignore) {
return null;
}
}
}
Which is then used as:
<p:inputText ... converter="lenientDoubleConverter" />
Unrelated to the concrete problem, please note that client side validation/conversion is absolutely not reliable. As JavaScript runs fully at the client side, the enduser has full control over the code being executed. I.e. the enduser can easily disable, bypass, spoof it, etc. See also JSF2 Validation Clientside or Serverside?

When to use valueChangeListener or f:ajax listener?

What's the difference between the following two pieces of code - with regards to listener placement?
<h:selectOneMenu ...>
<f:selectItems ... />
<f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>
and
<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
<f:selectItems ... />
</h:selectOneMenu>
The valueChangeListener will only be invoked when the form is submitted and the submitted value is different from the initial value. It's thus not invoked when only the HTML DOM change event is fired. If you would like to submit the form during the HTML DOM change event, then you'd need to add another <f:ajax/> without a listener(!) to the input component. It will cause a form submit which processes only the current component (as in execute="#this").
<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
<f:selectItems ... />
<f:ajax />
</h:selectOneMenu>
When using <f:ajax listener> instead of valueChangeListener, it would by default executed during the HTML DOM change event already. Inside UICommand components and input components representing a checkbox or radiobutton, it would be by default executed during the HTML DOM click event only.
<h:selectOneMenu value="#{bean.value}">
<f:selectItems ... />
<f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>
Another major difference is that the valueChangeListener method is invoked during the end of the PROCESS_VALIDATIONS phase. At that moment, the submitted value is not been updated in the model yet. So you cannot get it by just accessing the bean property which is bound to the input component's value. You need to get it by ValueChangeEvent#getNewValue(). The old value is by the way also available by ValueChangeEvent#getOldValue().
public void changeListener(ValueChangeEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
// ...
}
The <f:ajax listener> method is invoked during INVOKE_APPLICATION phase. At that moment, the submitted value is already been updated in the model. You can just get it by directly accessing the bean property which is bound to the input component's value.
private Object value; // +getter+setter.
public void ajaxListener(AjaxBehaviorEvent event) {
System.out.println(value); // Look, (new) value is already set.
}
Also, if you would need to update another property based on the submitted value, then it would fail when you're using valueChangeListener as the updated property can be overridden by the submitted value during the subsequent UPDATE_MODEL_VALUES phase. That's exactly why you see in old JSF 1.x applications/tutorials/resources that a valueChangeListener is in such construct been used in combination with immediate="true" and FacesContext#renderResponse() to prevent that from happening. After all, using the valueChangeListener to execute business actions has actually always been a hack/workaround.
Summarized: Use the valueChangeListener only if you need to intercept on the actual value change itself. I.e. you're actually interested in both the old and the new value (e.g. to log them).
public void changeListener(ValueChangeEvent event) {
changeLogger.log(event.getOldValue(), event.getNewValue());
}
Use the <f:ajax listener> only if you need to execute a business action on the newly changed value. I.e. you're actually interested in only the new value (e.g. to populate a second dropdown).
public void ajaxListener(AjaxBehaviorEvent event) {
selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}
If you're actually also interested in the old value while executing a business action, then fall back to valueChangeListener, but queue it to the INVOKE_APPLICATION phase.
public void changeListener(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println(newValue.equals(value)); // true
// ...
}
for the first fragment (ajax listener attribute):
The "listener" attribute of an ajax tag is a method that is called on the server side every time the ajax function happens on the client side. For instance, you could use this attribute to specify a server side function to call every time the user pressed a key
but the second fragment (valueChangeListener) :
The ValueChangeListener will only be called when the form is submitted, not when the value of the input is changed
*you might like to view this handy answer

JSF2 Cross Field Validation Failure Loop

Requirements in an app I'm developing say that when performing a search, the user should not be able to search for City without entering State, and vice versa, they shouldn't be able to search for State without entering City.
search.xhtml
<h:inputText id="city" binding="#{city}" value="#{search.city}" validator="#{search.validateCity}">
<f:attribute name="state" value="#{state}"/>
</h:inputText>
<h:inputText id="state" binding="#{state}" value="#{search.state}" validator="#{search.validateState}">
<f:attribute name="city" value="#{city}"/>
</h:inputText>
Search.java
public void validateCity(FacesContext context, UIComponent component, Object convertedValue) {
UIInput stateComponent = (UIInput) component.getAttributes().get("state");
String state = (String) stateComponent.getValue();
if(convertedValue.toString().length() > 0) {
if(state.length() < 1) {
throw new ValidatorException(new FacesMessage("Please enter State."));
}
}
}
public void validateState(FacesContext context, UIComponent component, Object convertedValue) {
UIInput cityComponent = (UIInput) component.getAttributes().get("city");
String city = (String) cityComponent.getValue();
if(convertedValue.toString().length() > 0) {
if(city.length() < 1) {
throw new ValidatorException(new FacesMessage("Please enter City."));
}
}
}
I've simplified down my code to show what I attempted with the standard cross field validation method. However, the problem I'm hitting is that in the validation phase, both City and State are showing Validation errors, I'm guessing because the two validators are getting in each others' way and therefore creating a loop of failure.
Is there a workaround I can use to get around this?
Thanks.
The components are validated in the order as they are declared in the component tree.
When you call UIInput#getValue() on a component which isn't validated yet, then it'll return null. Also, when you call UIInput#getValue() on a component which is already validated and been marked invalid, then it'll return null (or the old model value).
If you want to get the value of the second component during validation of the first component, then you should be using UIInput#getSubmittedValue() instead of UIInput#getValue(). You should only keep in mind that this returns the unconverted String.
Alternatively, you could take a look at OmniFaces <o:validateAllOrNone> component.
<h:inputText id="city" value="#{search.city}" />
<h:inputText id="state" value="#{search.state}" />
<o:validateAllOrNone id="cityAndState" components="city state" message="Please fill both city and state." />
<h:message for="cityAndState" />

Resources