SeamFramework.orgCommunity Documentation
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="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:s="http://jboss.org/seam/faces"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h1>Welcome to Seam Faces!</h1>
<p>
This view imports the Seam Faces component library.
Read on to discover what components it provides.
</p>
</html>
All Seam Faces components use the following namespace:
http://jboss.org/seam/faces
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="#{bean.city}" />
<h:inputText id="state" value="#{bean.state}" />
<h:inputText id="zip" value="#{bean.zip}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
<s:validateForm validatorId="locationValidator" />
</h:form>
The corresponding Validator for the example above would look something like this:
@FacesValidator("locationValidator")
public class LocationValidator implements Validator
{
@Inject
Directory directory;
@Inject
@InputField
private Object city;
@Inject
@InputField
private Object state;
@Inject
@InputField
private ZipCode zip;
@Override
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."));
}
}
}
You may inject the correct type directly.
@Inject @InputField private ZipCode zip;
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="#{bean.city}" />
<h:inputText id="stateId" value="#{bean.state}" />
<h:inputText id="zip" value="#{bean.zip}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submitPost}" />
<s:validateForm fields="city=cityId state=stateId" validatorId="locationValidator" />
</h:form>
Notice that "zip" will still be referenced normally; you need only specify aliases for fields that differ in name from the Validator @InputFields.
Using @InputField("customID")
with an ID override can also be used to specify a
custom ID, instead of using the default: the name of the field. This gives you the ability to
change the name of the private field, without worrying about changing the name of input fields in
the View itself.
@Inject @InputField("state") private String sectorTwo;
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.
@FacesValidator("fooValidator")
public class FooValidator implements Validator {
@Inject
private InputElement<String> firstNameElement;
@Inject
private InputElement<String> lastNameElement;
@Inject
private InputElement<Date> startDateElement;
@Inject
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:
http://localhost:8080/blog/entry.jsf?id=10
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:metadata>
<f:viewParam name="id" value="#{blogManager.entryId}"/>
<s:viewAction action="#{blogManager.loadEntry}"/>
</f:metadata>
The view action component must be declared as a child of the view metadata facet (i.e.,
<f:metadata>
) so that it gets incorporated into the JSF lifecycle on both
non-faces (initial) requests and faces (postback) requests. If you put it anywhere else in the
page, the behavior is undefined.
The JSF 2 specification specifies that there must be at least one view parameter for the view metadata facet to be processed on an initial request. This requirement was introduced into the JSF specification inadvertently. But not to worry. Seam Faces inserts a placeholder view parameter into the view metadata if it contains other components but no view parameters. That means you can use a view action without a view parameter, contrary to the JSF specification.
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.
<navigation-rule>
<from-view-id>/entry.xhtml</from-view-id>
<navigation-case>
<from-action>#{blogManager.loadEntry}</from-action>
<if>#{empty entry}</if>
<to-view-id>/home.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
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"/>
The valid phases for a view action are:
APPLY_REQUEST_VALUES
(default if immediate="true"
)
UPDATE_MODEL_VALUES
PROCESS_VALIDATIONS
INVOKE_APPLICATION
(default)
If the phase is set, it takes precedence over the immediate flag.
The purpose of the view action is similar to use of the PreRenderViewEvent. In fact, the code to load a blog entry before the page is rendered could be written as:
<f:metadata>
<f:viewParam name="id" value="#{blogManager.entryId}"/>
<f:event type="preRenderView" listener="#{blogManager.loadEntry}"/>
</f:metadata>
However, the view action has several important advantages:
It's lightweight
It's timing can be controlled
It's contextual
It can trigger navigation
View actions are lightweight because they get processed on a non-faces (initial) request before the full component tree is built. When the view actions are invoked, the component tree only contains view metadata.
As demonstrated above, you can specify a prerequisite condition for invoking the view action, control whether it's invoked on postback, specify the phase in which it's invoked and tie the invocation into the declarative navigation system. The PreRenderViewEvent is quite basic in comparison.
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:
http://java.sun.com/jsf/composite/components/seamfaces.
xmlns:sc="http://java.sun.com/jsf/composite/components/seamfaces"
<sc:inputContainer label="name" id="name">
<h:inputText id="input" value="#{person.name}"/>
</sc:inputContainer>
If you want to define your own composite component, follow this definition example (minus layout):
<cc:interface componentType="org.jboss.seam.faces.InputContainer"/>
<cc:implementation>
<h:outputLabel id="label" value="#{cc.attrs.label}:" styleClass="#{cc.attrs.invalid ? 'invalid' : ''}">
<h:outputText styleClass="required" rendered="#{cc.attrs.required}" value="*"/>
</h:outputLabel>
<h:panelGroup>
<cc:insertChildren/>
</h:panelGroup>
<h:message id="message" errorClass="invalid message" rendered="#{cc.attrs.invalid}"/>
</cc:implementation>
it's currently required to wrap the insertChildren tag with a jsf panelGroup. Please see http://java.net/jira/browse/JAVASERVERFACES-1991 for more details.
NOTE: Firefox does not properly associate a label with the target input if the input id
contains a colon (:), the default separator character in JSF. JSF 2 allows developers to
set the value via an initialization parameter (context-param in web.xml) keyed to
javax.faces.SEPARATOR_CHAR
. We recommend that you override this setting to make the
separator an underscore (_).