802 lines
39 KiB
XML
802 lines
39 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter id="spring-faces">
|
|
<title>JSF Integration</title>
|
|
<sect1 id="spring-faces-introduction">
|
|
<title>Introduction</title>
|
|
<para>
|
|
Spring Faces is Spring's JSF integration module that simplifies using JSF with Spring. It lets you use the
|
|
JSF UI Component Model with Spring MVC and Spring Web Flow controllers.
|
|
</para>
|
|
<para>
|
|
Spring Faces also includes a small Facelets component library that provides Ajax and client-side validation
|
|
capabilities. This component library builds on Spring Javascript, a Javascript abstraction framework that
|
|
integrates Dojo as the underlying UI toolkit.
|
|
</para>
|
|
</sect1>
|
|
<sect1 id="spring-faces-integration">
|
|
<title>Spring-centric Integration Approach</title>
|
|
<para>
|
|
Spring Faces combines the strengths of JSF, its UI component model, with the strengths of Spring, its
|
|
controller and configuration model. This brings you all the strengths of JSF without any of the weaknesses.
|
|
</para>
|
|
<para>
|
|
Spring Faces provides a powerful supplement to a number of the standard JSF facilities, including:
|
|
<orderedlist>
|
|
<listitem>managed bean facility</listitem>
|
|
<listitem>scope management</listitem>
|
|
<listitem>event handling</listitem>
|
|
<listitem>navigation rules</listitem>
|
|
<listitem>easy modularization and packaging of views</listitem>
|
|
<listitem>cleaner URLs</listitem>
|
|
<listitem>model-level validation</listitem>
|
|
<listitem>client-side validation and UI enhancement</listitem>
|
|
<listitem>Ajax partial page updates and full navigation</listitem>
|
|
<listitem>progressive enhancement and graceful degradation</listitem>
|
|
</orderedlist>
|
|
|
|
Using these features will significantly reduce the amount of configuration required in faces-config.xml
|
|
while providing a cleaner separation between the view and controller layer and better modularization of your
|
|
application's functional responsibilities. These use of these features are outlined in the sections to
|
|
follow. As the majority of these features build on the flow definition language of Spring Web Flow, it is
|
|
assumed that you have an understanding of the foundations presented in
|
|
<link linkend="defining-flows">Defining Flows</link>
|
|
.
|
|
</para>
|
|
</sect1>
|
|
<sect1 id="spring-faces-config-web.xml">
|
|
<title>Configuring web.xml</title>
|
|
<para>
|
|
The first step to using Spring Faces is to route requests to the
|
|
<code>DispatcherServlet</code>
|
|
in the
|
|
<code>web.xml</code>
|
|
file. In this example, we map all URLs that begin with
|
|
<code>/spring/</code>
|
|
to the servlet. The servlet needs to be configured. An
|
|
<code>init-param</code>
|
|
is used in the servlet to pass the
|
|
<code>contextConfigLocation</code>
|
|
. This is the location of the Spring configuration for your application.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<servlet>
|
|
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
|
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
|
<init-param>
|
|
<param-name>contextConfigLocation</param-name>
|
|
<param-value>/WEB-INF/web-application-config.xml</param-value>
|
|
</init-param>
|
|
<load-on-startup>1</load-on-startup>
|
|
</servlet>
|
|
|
|
<servlet-mapping>
|
|
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
|
|
<url-pattern>/spring/*</url-pattern>
|
|
</servlet-mapping>]]>
|
|
</programlisting>
|
|
<para>
|
|
In order for JSF to bootstrap correctly, the
|
|
<code>FacesServlet</code>
|
|
must be configured in
|
|
<code>web.xml</code>
|
|
as it normally would even though you generally will not need to route requests through it at all when using
|
|
Spring Faces.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<!-- Just here so the JSF implementation can initialize, *not* used at runtime -->
|
|
<servlet>
|
|
<servlet-name>Faces Servlet</servlet-name>
|
|
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
|
|
<load-on-startup>1</load-on-startup>
|
|
</servlet>
|
|
|
|
<!-- Just here so the JSF implementation can initialize -->
|
|
<servlet-mapping>
|
|
<servlet-name>Faces Servlet</servlet-name>
|
|
<url-pattern>*.faces</url-pattern>
|
|
</servlet-mapping>]]>
|
|
</programlisting>
|
|
<para>
|
|
When using the Spring Faces components, you also need to configure the Spring JavaScript
|
|
<code>ResourceServlet</code>
|
|
so that CSS and JavaScript resources may be output correctly by the components. This servlet must be mapped
|
|
to /resources/* in order for the URL's rendered by the components to function correctly.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<!-- Serves static resource content from .jar files such as spring-faces.jar -->
|
|
<servlet>
|
|
<servlet-name>Resource Servlet</servlet-name>
|
|
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
|
|
<load-on-startup>0</load-on-startup>
|
|
</servlet>
|
|
|
|
<!-- Map all /resources requests to the Resource Servlet for handling -->
|
|
<servlet-mapping>
|
|
<servlet-name>Resources Servlet</servlet-name>
|
|
<url-pattern>/resources/*</url-pattern>
|
|
</servlet-mapping>]]>
|
|
</programlisting>
|
|
<para>
|
|
The Spring Faces components require the use of Facelets instead of JSP, so the typical Facelets
|
|
configuration must be added as well when using these components.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
!-- Use JSF view templates saved as *.xhtml, for use with Facelets -->
|
|
<context-param>
|
|
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
|
|
<param-value>.xhtml</param-value>
|
|
</context-param>]]>
|
|
</programlisting>
|
|
</sect1>
|
|
<sect1 id="spring-faces-webflow-config">
|
|
<title>Configuring Web Flow to render JSF views</title>
|
|
<para>
|
|
The next step is to configure Web Flow to render JSF views. To do this, in your Spring Web Flow
|
|
configuration include the
|
|
<code>faces</code>
|
|
namespace and link in the faces
|
|
<code>flow-builder-services</code>
|
|
:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
|
|
xmlns:faces="http://www.springframework.org/schema/faces"
|
|
xsi:schemaLocation="
|
|
http://www.springframework.org/schema/beans
|
|
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
|
http://www.springframework.org/schema/webflow-config
|
|
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
|
|
http://www.springframework.org/schema/faces
|
|
http://www.springframework.org/schema/faces/spring-faces-2.0.xsd">
|
|
|
|
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
|
|
<webflow:flow-executor id="flowExecutor" />
|
|
|
|
<!-- The registry of executable flow definitions -->
|
|
<webflow:flow-registry id="flowRegistry" flow-builder-services="facesFlowBuilderServices">
|
|
<webflow:flow-location path="/WEB-INF/flows/main/main.xml" />
|
|
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
|
|
</webflow:flow-registry>
|
|
|
|
<!-- Configures the Spring Web Flow JSF integration -->
|
|
<faces:flow-builder-services id="facesFlowBuilderServices" />
|
|
|
|
</beans>]]>
|
|
</programlisting>
|
|
<para>
|
|
The <code>faces:flow-builder-services</code> tag also several other defaults appropriate for a JSF environment.
|
|
Specifically, the Unified EL is configured as the default Expression Language.
|
|
</para>
|
|
<para>
|
|
See the swf-booking-faces reference application in the distribution for a complete working example.
|
|
</para>
|
|
</sect1>
|
|
<sect1 id="spring-faces-config">
|
|
<title>Configuring faces-config.xml</title>
|
|
<para>
|
|
The only configuration needed in
|
|
<code>faces-config.xml</code>
|
|
is specific to the use of Facelets. If you are using JSP and not using the Spring Faces components, you do
|
|
not need to add anything specific to Spring Faces to your
|
|
<code>faces-config.xml</code>
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<faces-config>
|
|
<application>
|
|
<!-- Enables Facelets -->
|
|
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
|
|
</application>
|
|
</faces-config>]]>
|
|
</programlisting>
|
|
</sect1>
|
|
<sect1 id="spring-faces-managed-beans">
|
|
<title>Replacing the JSF Managed Bean Facility</title>
|
|
<para>
|
|
Spring Faces allows you to completely replace the JSF managed bean facility with a combination of
|
|
flow-managed variables and Spring managed beans. It gives you a good deal more control over the lifecycle of
|
|
your managed objects with well-defined hooks for initialization and execution of your domain model.
|
|
Additionally, since you are presumably already using Spring for your business layer, it reduces the
|
|
conceptual overhead of having to maintain two different managed bean models.
|
|
</para>
|
|
<para>
|
|
In doing pure JSF development, you will quickly find that request scope is not long-lived enough for storing
|
|
conversational model objects that drive complex event-driven views. The only available option is to begin
|
|
putting things into session scope, with the extra burden of needing to clean the objects up before
|
|
progressing to another view or functional area of the application. What is really needed is a managed scope
|
|
that is somewhere between request and session scope. Fortunately web flow provides such extended facilities.
|
|
</para>
|
|
<sect2 id="spring-faces-flow-variables">
|
|
<title>Using Flow Variables</title>
|
|
<para>
|
|
The easiest and most natural way to declare and manage the model is through the use of
|
|
<link linkend="flow-variables">flow variables</link>
|
|
. You can declare these variables at the beginning of the flow:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>]]>
|
|
</programlisting>
|
|
<para>and then reference this variable in one of the flow's JSF view templates through EL:</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<h:inputText id="searchString" value="#{searchCriteria.searchString}"/>]]>
|
|
</programlisting>
|
|
<para>
|
|
Note that you do not need to prefix the variable with its scope when referencing it from the template
|
|
(though you can do so if you need to be more specific). As with standard JSF beans, all available scopes
|
|
will be searched for a matching variable, so you could change the scope of the variable in your flow
|
|
definition without having to modify the EL expressions that reference it.
|
|
</para>
|
|
<para>
|
|
You can also define view instance variables that are scoped to the current view and get cleaned up
|
|
automatically upon transitioning to another view. This is quite useful with JSF as views are often
|
|
constructed to handle multiple in-page events across many requests before transitioning to another view.
|
|
</para>
|
|
<para>
|
|
To define a view instance variable, you can use the
|
|
<code>var</code>
|
|
element inside a
|
|
<code>view-state</code>
|
|
definition:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="enterSearchCriteria">
|
|
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="spring-faces-spring-beans">
|
|
<title>Using Scoped Spring Beans</title>
|
|
<para>
|
|
Though defining autowired flow instance variables provides nice modularization and readability,
|
|
occasions may arise where you want to utilize the other capabilities of the Spring container such as
|
|
AOP. In these cases, you can define a bean in your Spring ApplicationContext and give it a specific web
|
|
flow scope:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<bean id="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria" scope="flow"/>]]>
|
|
</programlisting>
|
|
<para>
|
|
The major difference with this approach is that the bean will not be fully initialized until it is first
|
|
accessed via an EL expression. This sort of lazy instantiation via EL is quite similar to how JSF
|
|
managed beans are typically allocated.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="faces-manipulating-model">
|
|
<title>Manipulating The Model</title>
|
|
<para>
|
|
The need to initialize the model before view rendering (such as by loading persistent entities from a
|
|
database) is quite common, but JSF by itself does not provide any convenient hooks for such
|
|
initialization. The flow definition language provides a natural facility for this through its
|
|
<link linkend="flow-actions">Actions</link>
|
|
. Spring Faces provides some extra conveniences for converting the outcome of an action into a
|
|
JSF-specific data structure. For example:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<on-render>
|
|
<evaluate expression="bookingService.findBookings(currentUser.name)"
|
|
result="viewScope.bookings" result-type="dataModel" />
|
|
</on-render>]]>
|
|
</programlisting>
|
|
<para>
|
|
This will take the result of the
|
|
<code>bookingService.findBookings</code>
|
|
method an wrap it in a custom JSF DataModel so that the list can be used in a standard JSF DataTable
|
|
component:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<h:dataTable id="bookings" styleClass="summary" value="#{bookings}" var="booking"
|
|
rendered="#{bookings.rowCount > 0}">
|
|
<h:column>
|
|
<f:facet name="header">Name</f:facet>
|
|
#{booking.hotel.name}
|
|
</h:column>
|
|
<h:column>
|
|
<f:facet name="header">Confirmation number</f:facet>
|
|
#{booking.id}
|
|
</h:column>
|
|
<h:column>
|
|
<f:facet name="header">Action</f:facet>
|
|
<h:commandLink id="cancel" value="Cancel" action="cancelBooking" />
|
|
</h:column>
|
|
</h:dataTable>]]>
|
|
</programlisting>
|
|
<para>
|
|
The custom DataModel provides some extra conveniences such as being serializable for storage beyond
|
|
request scope and access to the currently selected row in EL expressions. For example, on postback from
|
|
a view where the action event was fired by a component within a DataTable, you can take action on the
|
|
selected row's model instance:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<transition on="cancelBooking">
|
|
<evaluate expression="bookingService.cancelBooking(bookings.selectedRow)" />
|
|
</transition>]]>
|
|
</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="spring-faces-event-handling">
|
|
<title>Handling JSF Events With Spring Web Flow</title>
|
|
<para>
|
|
Spring Web Flow allows you to handle JSF action events in a decoupled way, requiring no direct dependencies
|
|
in your Java code on JSF API's. In fact, these events can often be handled completely in the flow definiton
|
|
language without requiring any custom Java action code at all. This allows for a more agile development
|
|
process since the artifacts being manipulated in wiring up events (JSF view templates and SWF flow
|
|
definitions) are instantly refreshable without requiring a build and re-deploy of the whole application.
|
|
</para>
|
|
<sect2 id="spring-faces-in-page-events">
|
|
<title>Handling JSF In-page Action Events</title>
|
|
<para>
|
|
A simple but common case in JSF is the need to signal an event that causes manipulation of the model in
|
|
some way and then redisplays the same view to reflect the changed state of the model. The flow
|
|
definition language has special support for this in the
|
|
<code>transition</code>
|
|
element.
|
|
</para>
|
|
<para>
|
|
A good example of this is a table of paged list results. Suppose you want to be able to load and display
|
|
only a portion of a large result list, and allow the user to page through the results. The initial
|
|
<code>view-state</code>
|
|
definition to load and display the list would be:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="reviewHotels">
|
|
<on-render>
|
|
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
|
result="viewScope.hotels" result-type="dataModel" />
|
|
</on-render>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
You construct a JSF DataTable that displays the current
|
|
<code>hotels</code>
|
|
list, and then place a "More Results" link below the table:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<h:commandLink id="nextPageLink" value="More Results" action="next"/>]]>
|
|
</programlisting>
|
|
<para>
|
|
This commandLink signals a "next" event from its action attribute. You can then handle the event by
|
|
adding to the
|
|
<code>view-state</code>
|
|
definition:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="reviewHotels">
|
|
<on-render>
|
|
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
|
result="viewScope.hotels" result-type="dataModel" />
|
|
</on-render>
|
|
<transition on="next">
|
|
<evaluate expression="searchCriteria.nextPage()" />
|
|
</transition>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
Here you handle the "next" event by incrementing the page count on the searchCriteria instance. The
|
|
<code>on-render</code>
|
|
action is then called again with the updated criteria, which causes the next page of results to be
|
|
loaded into the DataModel. The same view is re-rendered since there was no
|
|
<code>to</code>
|
|
attribute on the
|
|
<code>transition</code>
|
|
element, and the changes in the model are reflected in the view.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-action-events">
|
|
<title>Handling JSF Action Events</title>
|
|
<para>
|
|
The next logical level beyond in-page events are events that require navigation to another view, with
|
|
some manipulation of the model along the way. Achieving this with pure JSF would require adding a
|
|
navigation rule to faces-config.xml and likely some intermediary Java code in a JSF managed bean (both
|
|
tasks requiring a re-deploy). With the flow defintion language, you can handle such a case concisely in
|
|
one place in a quite similar way to how in-page events are handled.
|
|
</para>
|
|
<para>
|
|
Continuing on with our use case of manipulating a paged list of results, suppose we want each row in the
|
|
displayed DataTable to contain a link to a detail page for that row instance. You can add a column to
|
|
the table containing the following
|
|
<code>commandLink</code>
|
|
component:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<h:commandLink id="viewHotelLink" value="View Hotel" action="select"/>]]>
|
|
</programlisting>
|
|
<para>
|
|
This raises the "select" event which you can then handle by adding another
|
|
<code>transition</code>
|
|
element to the existing
|
|
<code>view-state</code>
|
|
:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="reviewHotels">
|
|
<on-render>
|
|
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
|
result="viewScope.hotels" result-type="dataModel" />
|
|
</on-render>
|
|
<transition on="next">
|
|
<evaluate expression="searchCriteria.nextPage()" />
|
|
</transition>
|
|
<transition on="select" to="reviewHotel">
|
|
<set name="flowScope.hotel" value="hotels.selectedRow" />
|
|
</transition>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
Here the "select" event is handled by pushing the currently selected hotel instance from the DataTable
|
|
into flow scope, so that it may be referenced by the "reviewHotel"
|
|
<code>view-state</code>
|
|
.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-model-validation">
|
|
<title>Performing Model Validation</title>
|
|
<para>
|
|
JSF provides useful facilities for validating input at field-level before changes are applied to the
|
|
model, but when you need to then perform more complex validation at the model-level after the updates
|
|
have been applied, you are generally left with having to add more custom code to your JSF action methods
|
|
in the managed bean. Validation of this sort is something that is generally a responsibility of the
|
|
domain model itself, but it is difficult to get any error messages propagated back to the view without
|
|
introducing an undesirable dependency on the JSF API in your domain layer.
|
|
</para>
|
|
<para>
|
|
With Spring Faces, you can utilize the generic and low-level
|
|
<code>MessageContext</code>
|
|
in your business code and any messages added there will then be available to the
|
|
<code>FacesContext</code>
|
|
at render time.
|
|
</para>
|
|
<para>
|
|
For example, suppose you have a view where the user enters the necessary details to complete a hotel
|
|
booking, and you need to ensure the Check In and Check Out dates adhere to a given set of business
|
|
rules. You can invoke such model-level validation from a
|
|
<code>transition</code>
|
|
element:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="enterBookingDetails">
|
|
<transition on="proceed" to="reviewBooking">
|
|
<evaluate expression="booking.validateEnterBookingDetails(messageContext)" />
|
|
</transition>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
Here the "proceed" event is handled by invoking a model-level validation method on the booking instance,
|
|
passing the generic
|
|
<code>MessageContext</code>
|
|
instance so that messages may be recorded. The messages can then be displayed along with any other JSF
|
|
messages with the
|
|
<code>h:messages</code>
|
|
component,
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-ajax-events">
|
|
<title>Handling Ajax Events</title>
|
|
<para>
|
|
Spring Faces provides some special
|
|
<code>UICommand</code>
|
|
components that go beyond the standard JSF components by adding the ability to do Ajax-based partial
|
|
view updates. These components degrade gracefully so that the flow will still be fully functional by
|
|
falling back to full page refreshes if a user with a less capable browser views the page.
|
|
</para>
|
|
<para>
|
|
Revisiting the earlier example with the paged table, you can change the "More Results" link to use an
|
|
Ajax request by replacing the standard
|
|
<code>commandButton</code>
|
|
with the Spring Faces version (note that the Spring Faces command components use Ajax by default, but
|
|
they can alternately be forced to use a normal form submit by setting ajaxEnabled="false" on the
|
|
component):
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<sf:commandLink id="nextPageLink" value="More Results" action="next" />]]>
|
|
</programlisting>
|
|
<para>
|
|
This event is handled just as in the non-Ajax case with the
|
|
<code>transition</code>
|
|
element, but now you will add a special
|
|
<code>render</code>
|
|
action that specifies which portions of the component tree need to be re-rendered:
|
|
</para>
|
|
<programlisting><![CDATA[
|
|
<view-state id="reviewHotels">
|
|
<on-render>
|
|
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
|
result="viewScope.hotels" result-type="dataModel" />
|
|
</on-render>
|
|
<transition on="next">
|
|
<evaluate expression="searchCriteria.nextPage()" />
|
|
<render fragments="hotels:searchResultsFragment" />
|
|
</transition>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
The
|
|
<code>fragments="hotels:searchResultsFragment"</code>
|
|
is an instruction that will be interpreted at render time, such that only the component with the JSF
|
|
clientId "hotels:searchResultsFragment" will be rendered and returned to the client. This fragment will
|
|
then be automatically replaced in the page. The
|
|
<code>fragments</code>
|
|
attribute can be a comma-delimited list of ids, with each id representing the root node of a subtree
|
|
(meaning the root node and all of its children) to be rendered. If the "next" event is fired in a
|
|
non-Ajax request (i.e., if JavaScript is disabled on the client), the
|
|
<code>render</code>
|
|
action will be ignored and the full page will be rendered as normal.
|
|
</para>
|
|
<para>
|
|
In addition to the Spring Faces
|
|
<code>commandLink</code>
|
|
component, there is a corresponding
|
|
<code>commandButton</code>
|
|
component with the same functionality. There is also a special
|
|
<code>ajaxEvent</code>
|
|
component that will raise a JSF action even in response to any client-side DOM event. See the Spring
|
|
Faces tag library docs for full details.
|
|
</para>
|
|
<para>
|
|
An additional built-in feature when using the Spring Faces Ajax components is the ability to have the
|
|
response rendered inside a rich modal popup widget by setting
|
|
<code>popup="true"</code>
|
|
on a
|
|
<code>view-state</code>
|
|
.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
|
|
<on-entry>
|
|
<render fragments="hotelSearchFragment" />
|
|
</on-entry>
|
|
<transition on="search" to="reviewHotels">
|
|
<evaluate expression="searchCriteria.resetPage()"/>
|
|
</transition>
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<para>
|
|
If the "changeSearchCriteria"
|
|
<code>view-state</code>
|
|
is reached as the result of an Ajax-request, the result will be rendered into a rich popup. If
|
|
JavaScript is unavailable, the request will be processed with a full browser refresh, and the
|
|
"changeSearchCriteria" view will be rendered as normal.
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="spring-faces-ui-controls">
|
|
<title>Enhancing The User Experience With Rich Web Forms</title>
|
|
<para>
|
|
JSF and Web Flow combine to provide and extensive server-side validation model for your web application, but
|
|
excessive roundtrips to the server to execute this validation and return error messages can be a tedious
|
|
experience for your users. Spring Faces provides a number of client-side rich validation controls that can
|
|
enhance the user experience by applying simple validations that give immediate feedback. Some simple
|
|
examples are illustrated below. See the Spring Faces taglib docs for a complete tag reference.
|
|
</para>
|
|
<sect2 id="spring-faces-text-validation">
|
|
<title>Validating a Text Field</title>
|
|
<para>
|
|
Simple client-side text validation can be applied with the
|
|
<code>clientTextValidator</code>
|
|
component:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<sf:clientTextValidator required="true">
|
|
<h:inputText id="creditCardName" value="#{booking.creditCardName}" required="true"/>
|
|
</sf:clientTextValidator>]]>
|
|
</programlisting>
|
|
<para>
|
|
This will apply client-side required validation to the child
|
|
<code>inputText</code>
|
|
component, giving the user a clear indicator if the field is left blank.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-number-validation">
|
|
<title>Validating a Numeric Field</title>
|
|
<para>
|
|
Simple client-side numeric validation can be applied with the
|
|
<code>clientNumberValidator</code>
|
|
component:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<sf:clientTextValidator required="true" regExp="[0-9]{16}"
|
|
invalidMessage="A 16-digit credit card number is required.">
|
|
<h:inputText id="creditCard" value="#{booking.creditCard}" required="true"/>
|
|
</sf:clientTextValidator>]]>
|
|
</programlisting>
|
|
<para>
|
|
This will apply client-side validation to the child
|
|
<code>inputText</code>
|
|
component, giving the user a clear indicator if the field is left blank, is not numeric, or does not
|
|
match the given regular expression.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-date-validation">
|
|
<title>Validating a Date Field</title>
|
|
<para>
|
|
Simple client-side date validation with a rich calendar popup can be applied with the
|
|
<code>clientDateValidator</code>
|
|
component:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<sf:clientDateValidator required="true" >
|
|
<h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true">
|
|
<f:convertDateTime pattern="yyyy-MM-dd" timeZone="EST"/>
|
|
</h:inputText>
|
|
</sf:clientDateValidator>]]>
|
|
</programlisting>
|
|
<para>
|
|
This will apply client-side validation to the child
|
|
<code>inputText</code>
|
|
component, giving the user a clear indicator if the field is left blank or is not a valid date.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="spring-faces-validate-all">
|
|
<title>Preventing an Invalid Form Submission</title>
|
|
<para>
|
|
The
|
|
<code>validateAllOnClick</code>
|
|
component can be used to intercept the "onclick" event of a child component and suppress the event if
|
|
all client-side validations do not pass.
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<sf:validateAllOnClick>
|
|
<sf:commandButton id="proceed" action="proceed" processIds="*" value="Proceed"/> 
|
|
</sf:validateAllOnClick>]]>
|
|
</programlisting>
|
|
<para>
|
|
This will prevent the form from being submitted when the user clicks the "proceed" button if the form is
|
|
invalid. When the validations are executed, the user is given clear and immediate indicators of the
|
|
problems that need to be corrected.
|
|
</para>
|
|
</sect2>
|
|
</sect1>
|
|
<sect1 id="spring-faces-component-libraries">
|
|
<title>Third-Party Component Library Integration</title>
|
|
<para>
|
|
Spring Faces strives to be compatible with any third-party JSF component library. By honoring all of the
|
|
standard semantics of the JSF specification within the SWF-driven JSF lifecycle, third-party libraries in
|
|
general should "just work". The main thing to remember is that configuration in web.xml will change slightly
|
|
since Spring Faces requests are not routed through the standard FacesServlet. Typically, anything that is
|
|
traditionally mapped to the FacesServlet should be mapped to the Spring DispatcherServlet instead. (You can
|
|
also map to both if for example you are migrating a legacy JSF application page-by-page.) In some cases, a
|
|
deeper level of integration can be achieved by configuring special flow services that are "aware" of a
|
|
particular component library, and these will be noted in the examples to follow.
|
|
</para>
|
|
<sect2 id="spring-faces-with-richfaces">
|
|
<title>Rich Faces Integration</title>
|
|
<para>
|
|
To use the Rich Faces component library with Spring Faces, the following filter configuration is needed
|
|
in web.xml (in addition to the typical Spring Faces configuration):
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<filter>
|
|
<display-name>RichFaces Filter</display-name>
|
|
<filter-name>richfaces</filter-name>
|
|
<filter-class>org.ajax4jsf.Filter</filter-class>
|
|
</filter>
|
|
|
|
<filter-mapping>
|
|
<filter-name>richfaces</filter-name>
|
|
<servlet-name>Spring Web MVC Dispatcher Servlet</servlet-name>
|
|
<dispatcher>REQUEST</dispatcher>
|
|
<dispatcher>FORWARD</dispatcher>
|
|
<dispatcher>INCLUDE</dispatcher>
|
|
</filter-mapping>]]>
|
|
</programlisting>
|
|
<para>
|
|
For deeper integration (including the ability to have a view with combined use of the Spring Faces Ajax
|
|
components and Rich Faces Ajax components), configure the RichFacesAjaxHandler on your FlowController:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<bean id="flowController" class="org.springframework.webflow.mvc.servlet.FlowController">
|
|
<property name="flowExecutor" ref="flowExecutor" />
|
|
<property name="ajaxHandler">
|
|
<bean class="org.springframework.faces.richfaces.RichFacesAjaxHandler"/>
|
|
</property>
|
|
</bean>]]>
|
|
</programlisting>
|
|
<para>
|
|
RichFaces Ajax components can be used in conjunction with the
|
|
<code>render</code>
|
|
tag to render partial fragments on an Ajax request. Instead of embedding the ids of the components to be
|
|
re-rendered directly in the view template (as you traditionally do with Rich Faces), you can bind the
|
|
<code>reRender</code>
|
|
attribute of a RichFaces Ajax component to a special
|
|
<code>flowRenderFragments</code>
|
|
EL variable. For example, in your view template you can have a fragment that you would potentially like
|
|
to re-render in response to a particular event:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<h:form id="hotels">
|
|
<a4j:outputPanel id="searchResultsFragment">
|
|
<h:outputText id="noHotelsText" value="No Hotels Found" rendered="#{hotels.rowCount == 0}"/>
|
|
<h:dataTable id="hotels" styleClass="summary" value="#{hotels}" var="hotel" rendered="#{hotels.rowCount > 0}">
|
|
<h:column>
|
|
<f:facet name="header">Name</f:facet>
|
|
#{hotel.name}
|
|
</h:column>
|
|
<h:column>
|
|
<f:facet name="header">Address</f:facet>
|
|
#{hotel.address}
|
|
</h:column>
|
|
</h:dataTable>
|
|
</a4j:outputPanel>
|
|
</h:form>]]>
|
|
</programlisting>
|
|
<para>
|
|
then a RichFaces Ajax
|
|
<code>commandLink</code>
|
|
to fire the event:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<a4j:commandLink id="nextPageLink" value="More Results" action="next" reRender="#{flowRenderFragments}" />]]>
|
|
</programlisting>
|
|
<para>
|
|
and then in your flow definition a
|
|
<code>transition</code>
|
|
to handle the event:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<transition on="next">
|
|
<evaluate expression="searchCriteria.nextPage()" />
|
|
<render fragments="hotels:searchResultsFragment" />
|
|
</transition>]]>
|
|
</programlisting>
|
|
</sect2>
|
|
<sect2 id="spring-faces-with-trinidad">
|
|
<title>Apache MyFaces Trinidad Integration</title>
|
|
<para>
|
|
The Apache MyFaces Trinidad library has been tested with the Spring Faces integration and proven to fit
|
|
in nicely. Deeper integration to allow the Trinidad components and Spring Faces components to play
|
|
well together has not yet been attempted, but Trinidad provides a pretty thorough solution on its own
|
|
when used in conjunction with the Spring Faces integration layer.
|
|
</para>
|
|
<para>
|
|
Typical Trinidad + Spring Faces configuration is as follows in web.xml (in addition to the typical
|
|
Spring Faces configuration):
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<context-param>
|
|
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
|
|
<param-value>server</param-value>
|
|
</context-param>
|
|
|
|
<context-param>
|
|
<param-name>
|
|
org.apache.myfaces.trinidad.CHANGE_PERSISTENCE
|
|
</param-name>
|
|
<param-value>session</param-value>
|
|
</context-param>
|
|
|
|
<context-param>
|
|
<param-name>
|
|
org.apache.myfaces.trinidad.ENABLE_QUIRKS_MODE
|
|
</param-name>
|
|
<param-value>false</param-value>
|
|
</context-param>
|
|
|
|
<filter>
|
|
<filter-name>Trinidad Filter</filter-name>
|
|
<filter-class>
|
|
org.apache.myfaces.trinidad.webapp.TrinidadFilter
|
|
</filter-class>
|
|
</filter>
|
|
|
|
<filter-mapping>
|
|
<filter-name>Trinidad Filter</filter-name>
|
|
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
|
|
</filter-mapping>
|
|
|
|
<servlet>
|
|
<servlet-name>Trinidad Resource Servlet</servlet-name>
|
|
<servlet-class>
|
|
org.apache.myfaces.trinidad.webapp.ResourceServlet
|
|
</servlet-class>
|
|
</servlet>
|
|
|
|
<servlet-mapping>
|
|
<servlet-name>resources</servlet-name>
|
|
<url-pattern>/adf/*</url-pattern>
|
|
</servlet-mapping>
|
|
]]>
|
|
</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter> |