my input fields have an <f:ajax>
object attached to make validation via pressing TAB possible:
<h:inputText id="input2" value="#{bean.property}">
<f:validator validatorId="customValidator" />
<f:ajax execute="@form" render="@this" />
</h:inputText>
If there are any validation errors, they will be displayed via a custom component similar to <h:messages>
and the input boxes will be styled via a custom PhaseListener
.
I have implemented a custom Faces Validator to skip validation:
public class CustomValidator {
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
if (skipValidation(context)) {
return;
}
//Validation code goes here
}
This gives me the option to skip bean validation based on some condition, e.g. switching to another editor view and saving the (maybe wrong) data in the backing bean.
Now, my problem occurs when refreshing the current page (either via pressing F5, hitting the same link in the navigation bar or similar). The obviously wrong data in the backing is displayed correctly, as intended, but it should also be marked as invalid. Thus I need a way to invoke the validation on every page load (currently it's invoked via the <f:ajax>
objects only).
I tried fiddling with something like this
<f:ajax event="load" execute="editform" render="editform"/>
attached to <h:body>
. This works, but the site is drawn twice - which I wouldn't really care if there was no evil flickering due toe the page first being drawn, then being validated & updated via the ajax request.
I've searched for hours for a solution to this problem, maybe I just searched in the wrong direction. Any suggestions on how to fix this?
Don't use a @SessionScoped
bean to back a page; it's a JSF design-smell (if there's such a thing)
Use the preRenderView
event to trigger (or check) whatever validation logic you choose on page load. As a start:
<f:event type="preRenderView" listener="#{bean.validate}" rendered="#{facesContext.postBack}" />
<f:event/>
tag registers an event listener on your page. type="preRenderView"
attribute stipulates that the event should be executed just before the page is loadedrendered="#{facesContext.postBack}"
is going to ensure that the event is loaded only when the same page is being refreshed, and not when it's being loaded for the first timelistener
will refer to a backing bean method in which you carry out the validation that you require. In this listener, you can throw a ValidationException
and also queue FacesMessages
appropriatelyAlternatives:
You can reference the component's value in a viewParam
and attach the same validator to that component:
<f:metadata>
<f:viewParam name="theParam" rendered="#{facesContext.postBack}" value="param['form:input2']" validator="customValidator"/>
</f:metadata>
What this would do is attempt to resubmit the value of the input2
component as a view parameter. A view parameter (in this usage) is a UIComponent
like any other, and is subject to the same conversion/validation semantics.
You could also pass the entire component as an attribute on the viewParam
, if you want a more involved handling of the content of that component. First bind the input2
component to the page scope:
<h:inputText id="input2" binding="#{input2}" value="#{bean.property}">
<f:validator validatorId="customValidator" />
<f:ajax execute="@form" render="@this" />
</h:inputText>
Then pass the component as an attribute:
<f:metadata>
<f:viewParam name="theParam" rendered="#{facesContext.postBack}" validator="customValidator"/>
<f:attribute name="input2" value="#{input2}"/>
</f:metadata>
You can then retrieve the value in your validator:
UIInput input2= (UIInput)component.getAttributes().get("input2");
String input2Value= input2.getSubmittedValue().toString();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With