What is a JSF component binding? How can I use it?

Standard

One of the more flexible features of Java Server Faces is the ability to use bindings within a view. Most developers omit the binding attribute and rely on a component libraries magic to display errors to the end user. Using the JSF binding component it is possible to specify the binding view component name for the UIComponent in the java backing bean. This binding refers to an instance of UIComponent in the EL scope allowing you to access certain properties #{mybinding.clientId}, #{mybinding.value},  #{mybinding.valid},  #{mybinding.submittedValue}

For example, lets assume you have a selectOneMenu with a list of items. These items are U.S states, then below you have a inputText area where the end user enters his/her city. We do not want to allow them to enter a city that is not in a state.

The problem can be solved using bindings. Consider this solution that binds the selected state value to the city entered. When submitted the validator will have access to both components.

The View

<h:selectOneMenu binding="#{state}" value="#{myBean.selectedState}">
    <f:selectItems value="#{myBean.states}" />
</h:selectOneMenu>
<h:inputText value="#{myBean.city}">
    <f:attribute name="state" value="#{state.value}" />
    <f:validator validatorId="locationValidator" />
</h:inputText>

Backing validator

@FacesValidator("locationValidator")
public class LocationValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) {
        Object item = component.getAttributes().get("state");
        // Object value holds city name
    }

}

Using bindings as described provides a solution for more complex validation requirements.

Using bindings with Bootstrap 3

Arguably the most popular css library out is Boostrap 3. It provides a sleek look to web pages. Using component bindings you can easily marry JSF with Bootstrap CSS. This is because components have a isValid EL expression attached to them.

For example lets assume you have a inputText that requires a min length of 4 and a max of 6. If outside of that requirement you want the field to be marked as invalid.

To highlight invalid fields use the binding component together with a form-group. The form-group has a has-error CSS class that will highlight the field inside. Once the inputText validation fails and refreshes the screen it will call the EL expression #{name.valid} and render the has-error class.

<div class="form-group  #{!name.valid ? 'has-error' : 'none'}">
   <label for="name" class="control-label">Enter name</label>
   <h:inputText value="#{myController.name}" binding="#{name}" id="name" styleClass="form-control">
      <f:validateLength minimum="4" maximum="6" />
   </h:inputText>
</div>

Whats the difference between #{mybinding.value} and  #{mybinding.submittedValue}?

This can be answered by understanding that the order of components matters. JSF will process your components in the order they appear in the view. The backing javascript for JSF does this. So for example if I was to swap the order of my state / city example so that the selectOneMenu came after the city inputText I would need to use the bindings submittedValue as ordering would cause the binding to be empty on the validator.

JSF session based queued flash messages

Standard

This article is intended to expand on the first article “JSF Bootstrap Style Validation” by adding sessions to store messages between views.

Other examples use getFlash() or phase listeners to intercept the messages:

context.getExternalContext().getFlash().setKeepMessages(true);

This solution uses session map instead to store the messages in between views. Using this class will allow for messages to sit in the queue until they are rendered out, this could happen 1,2,3…. pages later.

These two methods when added to the existing getMessages and queueMessage methods add session based messaging. Saving messages is handled by storing all queued messages into the existing faces session map. Then when we restore all messages it grabs the messages from the session map and adds them back to the current facesContext for rendering.

    /**
     * This is our session name
     */
    private static final String sessionToken = "MULTIPAGE-MESSAGES";

    /**
     * Saves all messages queued to the sessionToken
     *
     * @param facesContext
     */
    private static void saveMessages(final FacesContext facesContext) {

        // get existing messages map from session
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        HashMap<String, List<FacesMessage>> messagesByIDMap = (HashMap<String, List<FacesMessage>>) sessionMap.get(sessionToken);

        // If it doesn't exist make one
        if (messagesByIDMap == null) {
            messagesByIDMap = new HashMap<>();
            sessionMap.put(sessionToken, messagesByIDMap);
        }

        // Iterate through all messages that are waiting in queue
        for (Iterator<String> iter = facesContext.getClientIdsWithMessages(); iter.hasNext();) {

            String clientId = iter.next();
            List<FacesMessage> messageList = messagesByIDMap.get(clientId);

            // If no messages for client ID yet create empty arrayList
            if (messageList == null) {
                messageList = new ArrayList<>();
                messagesByIDMap.put(clientId, messageList);
            }

            Iterator<FacesMessage> facesIterator = facesContext.getMessages(clientId);

            // Add all the messages for that client ID to the session
            while (facesIterator.hasNext()) {
                messageList.add(facesIterator.next());
            }
        }
    }

    /**
     * Finds all the messages in the sessionToken and adds them
     *
     * @param facesContext
     */
    private static void restoreMessages(final FacesContext facesContext) {

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        HashMap<String, List<FacesMessage>> messagesByIDMap = (HashMap<String, List<FacesMessage>>) sessionMap.remove(sessionToken);

        if (messagesByIDMap == null) {
            return;
        }

        // Add all messages by client ID back to the facesContext
        for (String clientID : messagesByIDMap.keySet()) {
            List<FacesMessage> messages = messagesByIDMap.get(clientID);
            for (FacesMessage facesMessage : messages) {
                facesContext.addMessage(clientID, facesMessage);
            }
        }
    }

Looking for the rest of the code? View the first article “JSF Bootstrap Style Validation” or checkout the code on found on Github.