5. Write the View Code

After we have defined the constraints in the Java EE model layer and the database layer, we need to take care of validation in the user interface. In particular, we need to make sure that the user gets informed about issues by rendering visual indicators and informative validation error messages.

5.1. Validation in the Create use case

The WebContent/views/books/create.xhtml file contains the JSF facelet code for creating a new Book record. We now use the JSF validator attribute for performing the uniqueness validation and JSF message elements for displaying validation error messages.

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="headerTitle">
    <h1>Create a new book record</h1>
  </ui:define>
  <ui:define name="main">
    <h:form id="createBookForm">
      <div>
        <h:outputLabel for="isbn" value="ISBN: ">
        <h:inputText id="isbn" value="#{book.isbn}" 
            validator="#{bookCtrl.checkIsbnAsId}" />
        </h:outputLabel>
        <h:message for="isbn" errorClass="error" />
      </div>  
      <div>
        <h:outputLabel for="title" value="Title: ">
        <h:inputText id="title" value="#{book.title}" />
        </h:outputLabel>
        <h:message for="title" errorClass="error" />
      </div>
      ...
    </h:form>
  </ui:define>
</ui:composition>

There are only a few changes compared to the same view used for the minimal app, where no validation was performed. The first change is the new h:message element which is bound to a specific form element by the for attribute. We create such an element for each of our form input elements. Notice that we don't have to do anything else for seeing the validation errors for all integrity constraint checks which are performed by using the (built-in and custom) Bean Validation annotations. As soon as a constraint validation fails, the message set by using the message property of the integrity constraint annotation (e.g. @Pattern, @NotNull, etc) is displayed in an HTML span element generated by JSF as a result of using the h:message element.

For all the integrity constraints we have used Bean Validation annotations, but for the uniqueness constraint we have used custom code, therefore no error message will be shown for it. In the view code we can see that a new attribute, validator in h:inputText, was used for the isbn input field. It specifies which custom method is used to perform validation of the provided value in this form field. In our case, we use the checkIsbnAsId method defined in the BookController as shown below:

public void checkIsbnAsId( FacesContext context, 
    UIComponent component, Object value) 
    throws ValidatorException {
  String isbn = (String) value;
  try {
    Book.checkIsbnAsId( em, isbn);
  } catch ( UniquenessConstraintViolation e) {
    throw new ValidatorException( new FacesMessage(
        FacesMessage.SEVERITY_ERROR, e.getMessage(), 
        e.getMessage()));
  } catch ( MandatoryValueConstraintViolation e) {
    throw new ValidatorException( new FacesMessage(
        FacesMessage.SEVERITY_ERROR, e.getMessage(), 
        e.getMessage()));
  }
}

The controller's check method throws a ValidatorException which is also used to deliver the error message (the third parameter of the ValidatorException constructor) to the corresponding JSF facelet for being displayed in the UI. Methods used as JSF validators must have a specific syntax. The first two parameters of type FacesContext, respectively UIComponent are used by the container to invoke the method with references to the right view component and context, and they can be used in more complex validation methods. The last one, of type Object, represents the value to be validated by the method. This value has to be casted to the expected type (to String, in our example). It is important to know that, if a cast to a non-compatible type is performed, the validation method fails and an exception is thrown.

5.2. Validation in the Update use case

In the Update use case, the facelet file update.xhtml in WebContent/views/books was updated so it uses the h:message elements for being able to display validation errors:

<ui:composition template="/WEB-INF/templates/page.xhtml">
 <ui:define name="headerTitle">
  <h1>Update a book record</h1>
 </ui:define>
 <ui:define name="main">
  <h:form id="updateBookForm">
   <div>
    <h:outputLabel for="selectBook" value="Select book: ">
     <h:selectOneMenu id="selectBook" value="#{book.isbn}">
      ...
     </h:selectOneMenu>
    </h:outputLabel>
    <h:message for="selectBook" errorClass="error" />
   </div>
   <div>
    <h:outputLabel for="isbn" value="ISBN: ">
     <h:outputText id="isbn" value="#{book.isbn}" />
    </h:outputLabel>
   </div>
   ...
  </h:form>
 </ui:define>
</ui:composition>    

Since we do not allow to change the ISBN of a book, we create an output field for the isbn attribute with the JSF element h:outputText. This implies that no validation is performed.

Using an h:outputText element for showing the value of an entity attribute results in an HTML span element. This implies that the HTTP form submission message contains no information about that attribute. If the validation fails, we expect to see the form content together with the error messages. To get the expected result, we need to use the annotation @ViewScoped for the entity class pl.m.Book instead of @RequestScoped, otherwise our bean instance referenced by the book variable is initialized with a new value on every request, implying that the expression #{book.isbn} evaluates to null and the ISBN value is not displayed. The @ViewScoped annotation specifies that the entity bean is alive as long as the associated view is alive, so the ISBN value stored by the book is available during this time and it can be displayed in the view.

By contrast, h:inputText elements result in HTML input elements which are part of the form submission content, so the response contains the already existing values because these values are known in this case. This consideration shows that it is important to choose the right bean scope.