Sunday, January 22, 2012

Validation

What is validation?  Validation allows the application to verify data for correctness.  Typically this is done prior to updating model values and  invoking the application methods.  This means we may have methods that will only be invoked once the application has approved the data.  Validation also implies the communication back to the end user when such validation fails.  This is done via messages that are returned and potentially displayed.

One of our included xhtml files is demoDialog.xhtml:

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:p="http://primefaces.prime.com.tr/ui"
    xmlns:f="http://java.sun.com/jsf/core"      
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    <p:dialog widgetVar="demoDlg" width="700">
        <h:form id="demoForm">
            <h:panelGrid columns="1">
                <h:panelGrid id="demoPanelGrid" columns="3" cellpadding="5" width="700">
                    <h:outputLabel for="newdemoname" value="Name:" />
                    <h:inputText value="#{orderBean.newOrder.name}" id="newdemoname" validator="#{orderBean.validate}">
                    </h:inputText>
                    <h:message for="newdemoname" />
                    <h:outputLabel for="newdemoother" value="Other:" />
                    <h:inputText value="#{orderBean.otherName}" id="newdemoother" >
                        <f:validator validatorId="com.testit.NameValidator"/>
                        <f:ajax event="blur" render="msgmsg" onevent="ajaxSample"/>
                    </h:inputText>
                    <h:message id="msgmsg" for="newdemoother" />
                    <h:outputLabel for="newdemocounter" value="Initial Counter Value: " />
                    <h:inputText value="#{orderBean.newOrder.counter}" id="newdemocounter" required="true" validatorMessage="#{msgs.counterMessage}">
                        <f:validateLongRange minimum="1" maximum="1000"/>
                        <f:ajax event="blur" render="msgcounter"/>
                    </h:inputText>
                    <h:message id="msgcounter" for="newdemocounter" />
                    <h:outputLabel for="newdemomsg" value="Message:"/>
                    <h:outputText id="newdemomsg" value="#{orderBean.msg}"/>
                </h:panelGrid>
                <h:panelGrid columns="2" styleClass="alignTop">
                    <h:outputText value="Non Submit Buttons"/>
                    <h:commandButton type="button" value="Client Side Only - Execute JS method" onclick="showMessage('No Ajax : executed JavaScript with no form validation');"/>
                    <h:outputText value="Submit Buttons:" />
                    <h:panelGrid columns="5">
                        <h:outputText value="Without Ajax, Without Validation"/>
                        <h:commandButton value="Event" immediate="true" onclick="#{orderBean.sendAlert()}"/>
                        <h:commandButton value="Action" immediate="true" action="#{orderBean.actionCall}"/>
                        <h:commandButton value="ActionListener" immediate="true" actionListener="#{orderBean.listener}"/>
                        <h:commandButton value="All Three" immediate="true" action="#{orderBean.actionCall}" actionListener="#{orderBean.listener}" onclick="#{orderBean.sendAlert()}" />


                        <h:outputText value="Without Ajax, With Validation "/>
                        <h:commandButton value="Event" onclick="#{orderBean.sendAlert()}"/>
                        <h:commandButton value="Action" action="#{orderBean.actionCall}"/>
                        <h:commandButton value="ActionListener" actionListener="#{orderBean.listener}"/>
                        <h:commandButton value="All Three" action="#{orderBean.actionCall}" actionListener="#{orderBean.listener}" onclick="#{orderBean.sendAlert()}" />
                        <h:outputText value="With Ajax, Without Validation"/>
                        <h:commandButton value="Event" immediate="true" onclick="#{orderBean.sendAlert()}">
                            <f:ajax execute="newdemoname newdemomsg" render="demoForm" />
                        </h:commandButton>
                        <h:commandButton value="Action" immediate="true" action="#{orderBean.actionCall}">
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>
                        <h:commandButton value="ActionListener" immediate="true" actionListener="#{orderBean.listener}">
                            <f:ajax execute="@this" render="newdemomsg" />
                        </h:commandButton>
                        <h:commandButton value="All Three" immediate="true" action="#{orderBean.actionCall}" actionListener="#{orderBean.listener}" onclick="#{orderBean.sendAlert()}" >
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>


                        <h:outputText value="With Ajax, With Validation"/>
                        <h:commandButton value="Event" onclick="#{orderBean.sendAlert()}" >
                            <f:ajax execute="newdemoname newdemomsg" render="demoForm" />
                        </h:commandButton>
                        <h:commandButton value="Action" action="#{orderBean.actionCall}">
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>
                        <h:commandButton value="ActionListener" actionListener="#{orderBean.listener}">
                            <f:ajax execute="@this" render="newdemomsg" />
                        </h:commandButton>
                        <h:commandButton value="All Three" action="#{orderBean.actionCall}" actionListener="#{orderBean.listener}" onclick="#{orderBean.sendAlert()}" >
                            <f:ajax execute="@form" render="@form" />
                        </h:commandButton>
                    </h:panelGrid>
                </h:panelGrid>
            </h:panelGrid>
        </h:form>  
    </p:dialog>
</ui:composition>

This file give a wide variety of examples for both validation and Ajax.  Although the rules may be simple, the consequences can get a bit convoluted.  The first validator demonstrated is an example of method validation.  With our inputText component, we give a binding bean field, and a bean method to validate with:
    <h:inputText value="#{orderBean.newOrder.name}" id="newdemoname" validator="#{orderBean.validate}">

The signature of a validator method is: 
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException;

When the submit button is pushed, the validator is executed before the model values are updated.  If the validation fails, the model values are not updated, nor is the application invoked.

The second example of a validator is:
                    <h:inputText value="#{orderBean.otherName}" id="newdemoother" >
                        <f:validator validatorId="com.testit.NameValidator"/>
                        <f:ajax event="blur" render="msgmsg" onevent="ajaxSample"/>
                    </h:inputText>

Here we have created a validator class called NameValidator.

package com.sample;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
@FacesValidator("com.testit.NameValidator")
public class NameValidator implements Validator {
    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        if (value == null || !(value instanceof String) || ((String) value).isEmpty() || ((String) value).length() > 5) {
            FacesMessage message = new FacesMessage("Name must be a non empty String less then 6 characters.");
            throw new ValidatorException(message);
        }
    }
}

Our custom validator class must implement javax.faces.Validator and define the validate method.  Here we are checking requirements we could have checked with a standard validator, but we put it here for demonstration.

The third example of validation is:
 <h:inputText value="#{orderBean.newOrder.counter}" id="newdemocounter" required="true" validatorMessage="#{msgs.counterMessage}">
   <f:validateLongRange minimum="1" maximum="1000"/>
</h:inputText>

This uses a built in validator.  This says the field counter is a long and cannot be less then 1 nor greater then 1000.  There are 2 other built in validators -   DoubleRangeValidator for numeric values, and LengthValidator for strings.

There are 4 ways to validate in JSF:
    Built-in validation components discussed above - example 3.
    Validation methods in backing beans as discussed above - example 1.
    Custom validation components - example 2.
    Application-level validation - no example demonstrated.

Application level validation occurs after the other 3 and is located in the backing bean method.  The code can still return validation error messages, but the model values have been updated and the application invoked.

Validators can be used to validate input as well as application logic.  i.e. is the name unique, is the inventory on hand, does the user have money, etc.  The examples do not stop the user from entering invalid data.  I.e.  You can enter a 0 for the counter.  But, it does allow the application to verify correctness, report errors, and stop processing.

The other interesting piece of validation is that we can immediately validate the data upon input.  We do not need to have the user submit the form.  When validating the counter, we used ajax:
                         <f:ajax event="blur" render="msgcounter"/>
to render the message counter component after the focus was lost on the counter input field.  This allowed us to notify the user immediately when his input was invalid.

When these examples report an error, we need a way to communicate the error to the end user.  For the first example, we did this with the line:
       <h:message for="newdemoname" />

This places a message component on the page.  It renders the message registered for the component newdemoname.  The component newdemoname will have the message returned by the orderbean.validate method which was returned by the java code:
            FacesMessage message = new FacesMessage("Name is not unique.");
                throw new ValidatorException(message);                    

If no message was registered, nothing will be displayed.

Similarly, we implemented message components for the other two fields being validated.

No comments:

Post a Comment