Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validation on page refresh

Tags:

java

ajax

jsf

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?

like image 773
INK Avatar asked Oct 21 '22 08:10

INK


1 Answers

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}" />
  1. The <f:event/> tag registers an event listener on your page.
  2. The type="preRenderView" attribute stipulates that the event should be executed just before the page is loaded
  3. rendered="#{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 time
  4. listener 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 appropriately

Alternatives:

  1. 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.

  2. 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();
like image 94
kolossus Avatar answered Oct 23 '22 10:10

kolossus