final solution preventing validators to kick-in when not required

remember? it was first days, then hours, okay, today hours as well.. but I think with some help of you guys out there blogging also I found the final solution, and the times do get better and better, as I get more and more knowledge about the whole thing.

summary:

  • use the PhaseListener to catch the phase: PROCESS_VALIDATIONS
  • add some code to the beforePhase event (to disable the validators for all but the required events)
  • add some code to the afterPhase event (to enable the validators again)
  • add a recursive method which will enable/disable (ie toggle) all available validators
  • add event parameters to your buttons where you DO WANT TO trigger the validators

PhaseListener updates:

  • add this code in the beforePhase method, it will disable the validators if no event parameters (name=requireValidation, value=true) are present

FacesContext facesContext = event.getFacesContext();
try {
    // handle validators
    if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
// add validators if required
// i use:    
//CustomValidator v = new CustomValidator();
//v.handleValidators();

        // if not submitted via save button disable validators
        // get eventHandlerClientId
        String eventHandlerClientId = (String) facesContext
                .getExternalContext().getRequestParameterMap().get(
                        “$$xspsubmitid”);
        // extract eventHandlerId
        String eventHandlerId = eventHandlerClientId.substring(
                eventHandlerClientId.lastIndexOf(“:”) + 1,
                eventHandlerClientId.length());
        // get eventHandler “component”
        UIEventHandler eventHandler = (UIEventHandler) Util
                .findComponent(facesContext.getViewRoot(),
                        eventHandlerId);

        // find the parameters set via Edit Event parameters on the
        // Events page
        boolean skipValidation = true;
        List<Parameter> parameters = eventHandler.getParameters();
        if (null != parameters) {
            Iterator<Parameter> iterator = parameters.iterator();

            while (iterator.hasNext()) {
                Parameter par = iterator.next();
                if (par.getName().equals(“requireValidation”)
                        && par.getValue().equals(“true”)) {
                    skipValidation = false;
                    break;
                }
            }
        }
        if (skipValidation) {
            toggleValidators(facesContext.getViewRoot(), true);
        }

    }
} catch (Exception e) {
    // default disable validations for all other events which are not
    // “requireValidation=true” and which might throw an error while trying to get their event handler
    toggleValidators(facesContext.getViewRoot(), true);
}

  • add this code to the afterPhase, it will enable the validators again:

if (event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)) {
    FacesContext facesContext = event.getFacesContext();
    toggleValidators(facesContext.getViewRoot(), false);
}

  • add this method, it will recursively find all components where validators might be existing and enables/disables them. This method is most likely candidate for speed improvements: You could use as parent component the actual tab (for tabbed panels) being displayed, but then you would have to track which one is selected, I prefer to just run it on the whole tree to make it simple for now
private void toggleValidators(UIComponent parent, boolean toggle) {

    List<UIComponent> children = parent.getChildren();
    Iterator<UIComponent> iterator = children.iterator();
    while (iterator.hasNext()) {
        UIComponent child = iterator.next();
        if (child instanceof javax.faces.component.UIInput) {
            if (child instanceof com.ibm.xsp.component.UIInputEx) {
                UIInputEx input = (UIInputEx) child;
                input.setDisableValidators(toggle);
                if (0 < child.getChildCount()) {
                    toggleValidators(child, toggle);
                }
            } else if (child instanceof com.ibm.xsp.component.UISelectOneEx) {
                // radio/checkbox/combobox etc.
                UISelectOneEx input = (UISelectOneEx) child;
                input.setDisableValidators(toggle);
                if (0 < child.getChildCount()) {
                    toggleValidators(child, toggle);
                }
            }
        } else {
            if (0 < child.getChildCount()) {
                toggleValidators(child, toggle);
            }
        }
    }
}

  • add an event parameter to all your buttons where you do want to trigger the validation

et voilĂ .. you have a nice working form where validators are only triggered when really required (ie. upon save)!
no more stupid validations kicking-in when switching tabs, clicking buttons, refreshing via combo boxes panels etc…
as for the “Save as draft” button: I would recommend that you add a field on the form “flagIsDraft” so that you can take care of not postprocessing such documents
Kudos go to Tommy Valand and weihang chen, their “blogs” helped me get my way through!

Comments

  1. because I wanted it the other way around: instead of having to "disable" the validators I wanted to "enable" them specifically only on the 2 save buttons

  2. Ulrich, there are also other use cases, like the download control triggering validation when you delete a file. No way around it.

    Great stuff!

Leave a Reply

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.