SeamFramework.orgCommunity Documentation

Chapter 4. Seam Faces Components

4.1. Introduction
4.2. <s:validateForm>
4.3. <s:viewAction>
4.3.1. Motivation
4.3.2. Usage
4.3.3. View actions vs the PreRenderViewEvent
4.4. UI Input Container

While Seam Faces does not provide layout components or other UI-design related features, it does provide functional components designed to make developing JSF applications easier, more functional, more scalable, and more practical.

For layout and design components, take a look at RichFaces, a UI component library specifically tailored for easy, rich web-interfaces.

In order to use the Seam Faces components, you must first add the namespace to your view file, just like the standard JSF component libraries.

<html xmlns=""
   <h1>Welcome to Seam Faces!</h1>
      This view imports the Seam Faces component library.
      Read on to discover what components it provides.

On many occasions you might find yourself needing to compare the values of multiple input fields on a given page submit: confirming a password; re-enter password; address lookups; and so on. Performing cross-field form validation is simple - just place the <s:validateForm> component in the form you wish to validate, then attach your custom Validator.

<h:form id="locationForm">
   <h:inputText id="city" value="#{}" />
   <h:inputText id="state" value="#{bean.state}" />
   <h:inputText id="zip" value="#{}" />
   <h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
   <s:validateForm validatorId="locationValidator" />

The corresponding Validator for the example above would look something like this:


public class LocationValidator implements Validator
   Directory directory;
   private Object city;
   private Object state;
   private ZipCode zip;
   public void validate(final FacesContext context, final UIComponent comp, final Object values)
         throws ValidatorException
      if(!directory.exists(city, state, zip))
         throw new ValidatorException(
            new FacesMessage("Sorry, that location is not in our database. Please try again."));

Notice that the IDs of the inputText components match the IDs of your Validator @InputFields; each @Inject @InputField member will be injected with the value of the form input field who's ID matches the name of the variable.

In other words - the name of the @InputField annotated member variable will automatically be matched to the ID of the input component, unless overridden by using a field ID alias (see below.)

<h:form id="locationForm">
   <h:inputText id="cityId" value="#{}" />
   <h:inputText id="stateId" value="#{bean.state}" />
   <h:inputText id="zip" value="#{}" />
   <h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
   <s:validateForm fields="city=cityId state=stateId" validatorId="locationValidator" />

Notice that "zip" will still be referenced normally; you need only specify aliases for fields that differ in name from the Validator @InputFields.

An alternate way of accessing those fields on the validator by injecting an InputElement. It works similarly to @InputField, but stores the clientId and a JSF UIComponent, along with the field value.


public class FooValidator implements Validator {
    private InputElement<String> firstNameElement;
    private InputElement<String> lastNameElement;
    private InputElement<Date> startDateElement;
    private InputElement<Date> endDateElement;

Use get methods to access those information

public void validate(final FacesContext ctx, final UIComponent form, final Object value) throws ValidatorException {

        Date startDate = startDateElement.getValue();
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        if (startDate.before(calendar.getTime())) {
            String message = messageBuilder.get().key(new DefaultBundleKey("booking_checkInNotFutureDate"))
                    .targets(  startDateElement.getClientId()  ).build().getText();
            throw new ValidatorException(new FacesMessage(message));

The view action component (UIViewAction) is an ActionSource2 UIComponent that specifies an application-specific command (or action), using an EL method expression, to be invoked during one of the JSF lifecycle phases proceeding Render Response (i.e., view rendering).

View actions provide a lightweight front-controller for JSF, allowing the application to accommodate scenarios such as registration confirmation links, security and sanity checking a request (e.g., ensuring the resource can be loaded). They also allow JSF to work alongside action-oriented frameworks, and existing applications that use them.

JSF employs an event-oriented architecture. Listeners are invoked in response to user-interface events, such as the user clicking on a button or changing the value of a form input. Unfortunately, the most important event on the web, a URL request (initiated by the user clicking on a link, entering a URL into the browser's location bar or selecting a bookmark), has long been overlooked in JSF. Historically, listeners have exclusively been activated on postback, which has led to the common complaint that in JSF, "everything is a POST."

We want to change that perception.

Processing a URL request event is commonly referred to as bookmarkable or GET support. Some GET support was added to JSF 2.0 with the introduction of view parameters and the pre-render view event. View parameters are used to bind query string parameters to model properties. The pre-render view event gives the developer a window to invoke a listener immediately prior to the view being rendered.

That's a start.

Seam brings the GET support full circle by introducing the view action component. A view action is the compliment of a UICommand for an initial (non-faces) request. Like its cohort, it gets executed by default during the Invoke Application phase (now used on both faces and non-faces requests). A view action can optionally be invoked on postback as well.

View actions (UIViewAction) are closely tied to view parameters (UIViewParameter). Most of the time, the view parameter is used to populate the model with data that is consumed by the method being invoked by a UIViewAction component, much like form inputs populate the model with data to support the method being invoked by a UICommand component.

Let's consider a typical scenario in web applications. You want to display the contents of a blog entry that matches the identifier specified in the URL. We'll assume the URL is:


We'll use a view parameter to capture the identifier of the entry from the query string and a view action to fetch the entry from the database.

   <f:viewParam name="id" value="#{blogManager.entryId}"/>  
   <s:viewAction action="#{blogManager.loadEntry}"/>  

What do we do if the blog entry can't be found? View actions support declarative navigation just like UICommand components. So you can write a navigation rule that will be consulted before the page is rendered. If the rule matches, navigation occurs just as though this were a postback.

         <if>#{empty entry}</if>

After each view action is invoked, the navigation handler looks for a navigation case that matches the action's EL method signature and outcome. If a navigation case is matched, or the response is marked complete by the action, subsequent view actions are short-circuited. The lifecycle then advances appropriately.

By default, a view action is not executed on postback, since the primary intention of a view action is to support a non-faces request. If your application (or use case) is decidedly stateless, you may need the view action to execute on any type of request. You can enable the view action on postback using the onPostback attribute:

<s:viewAction action="#{blogManager.loadEntry}" onPostback="true"/>

You may only want the view action to be invoked under certain conditions. For instance, you may only need it to be invoked if the conversation is transient. For that, you can use the if attribute, which accepts an EL value expression:

<s:viewAction action="#{blogEditor.loadEntry}" if="#{conversation.transient}"/>

There are two ways to control the phase in which the view action is invoked. You can set the immediate attribute to true, which moves the invocation to the Apply Request Values phase instead of the default, the Invoke Application phase.

<s:viewAction action="#{sessionManager.validateSession}" immediate="true"/>

You can also just specify the phase directly, using the name of the phase constant in the PhaseId class (the case does not matter).

<s:viewAction action="#{sessionManager.validateSession}" phase="APPLY_REQUEST_VALUES"/>

If the phase is set, it takes precedence over the immediate flag.

UIInputContainer is a supplemental component for a JSF 2.0 composite component encapsulating one or more input components (EditableValueHolder), their corresponding message components (UIMessage) and a label (HtmlOutputLabel).

This component takes care of wiring the label to the first input and the messages to each input in sequence. It also assigns two implicit attribute values, "required" and "invalid" to indicate that a required input field is present and whether there are any validation errors, respectively. To determine if a input field is required, both the required attribute is consulted and whether the property has Bean Validation constraints.

Finally, if the "label" attribute is not provided on the composite component, the label value will be derived from the id of the composite component, for convenience.

There's a composite componente that ships with seam-faces under the url:

    <sc:inputContainer label="name" id="name">
        <h:inputText id="input" value="#{}"/>

If you want to define your own composite component, follow this definition example (minus layout):

<cc:interface componentType="org.jboss.seam.faces.InputContainer"/>
   <h:outputLabel id="label" value="#{cc.attrs.label}:" styleClass="#{cc.attrs.invalid ? 'invalid' : ''}">
     <h:outputText styleClass="required" rendered="#{cc.attrs.required}" value="*"/>
   <h:message id="message" errorClass="invalid message" rendered="#{cc.attrs.invalid}"/>