Search code examples
javajspjsfmyfaces

JSF page validation in Java and generated component IDs issue


I have a question according to <h:message for="" /> JSF object. I have a JSF 1.1 (yes, unfortunatelly) myFaces project. I've prepared a simple JSP file with label, inputText and message holder:

<h:form id="sacLinkForm">
    <cblFaces:outputLabel for="dedicatedCashAccountNumber"
        value="#{appBundle.KUSTA_DEDICATED_CASH_ACCOUNT_NUMBER}"/>
    <t:inputText id="dedicatedCashAccountNumber"
        value="#{createController.modelFE.sacLink.dedicatedCashAccountNumber}"/>
    <h:message id="dedicatedCashAccountNumberError"
        for="dedicatedCashAccountNumber" />
</h:form>

Also I've created simple validation method:

public static boolean validate(SACLink link) {
    boolean isError = false;
    FacesContext context = FacesContext.getCurrentInstance();
    FacesMessage message = new FacesMessage();
    message.setSeverity(FacesMessage.SEVERITY_ERROR);

    if (StringUtils.isEmpty(link.dedicatedCashAccountNumber)) {
        //MessageUtils.addErrorMessage("dedicatedCashAccountNumber", "Dedicated Cash Account Number is mandatory");
        message.setSummary("Field mandatory");
        message.setDetail("Dedicated Cash Account Number is mandatory");
        context.addMessage("sacLinkForm:dedicatedCashAccountNumber", message);
        isError = true;
    }
    return !isError;
}

My problem is with sacLinkForm:dedicatedCashAccountNumber because compiled JSP has generated id (f.e. _idJsp12:DynTab2:DynView2:tabK.....er:vKustaMa....Controller:sacLinkForm:dedicatedCashAccountNumber) and it probably causes that addMessage method doesn't correspond with h:message for=".." attribute.

So the message is not displayed correctly because java could not find correct h:message object. Is there any way how to set message to the correct h:message from Java? I assume that there is no way how to force 'constant' component ID in the JSF 1.1 pages.

Thank you.


Solution

  • You should not be manually performing validation in a JSF action method at all. You should utilize JSF builtin validation facilities. You should use a normal JSF Validator. Any ValidatorException which you throw from that will end up in the right message component.

    Replace that validate() method by the following class:

    public class AccountNumberValidator implements Validator {
    
        @Override
        public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
            if (StringUtils.isEmpty(value)) {
                FacesMessage message = new FacesMessage();
                message.setSeverity(FacesMessage.SEVERITY_ERROR);
                message.setSummary("Field mandatory");
                message.setDetail("Dedicated Cash Account Number is mandatory");
                throw new ValidatorException(message);
            }
        }
    
    }
    

    Register it in faces-config.xml as follows:

    <validator>
         <validator-id>accountNumberValidator</validator-id>
         <validator-class>com.example.AccountNumberValidator</validator-class>
    </validator>
    

    Use it in the view as follows:

    <t:inputText id="dedicatedCashAccountNumber"
        value="#{createController.modelFE.sacLink.dedicatedCashAccountNumber}"
        validator="accountNumberValidator" />
    

    That's it. Note that the JSF action method won't be invoked if validation (or conversion) has failed. No need for nonsensicial isError clutter as well.