Add reference docs tasks to Gradle build
- Move spring-webflow-reference to src/reference - Upgrade to Docbook 5 - Apply docbook plugin - Add tasks for generating distribution zip files
This commit is contained in:
854
src/reference/views.xml
Normal file
854
src/reference/views.xml
Normal file
@@ -0,0 +1,854 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xml:id="views">
|
||||
<title>Rendering views</title>
|
||||
<sect1 id="views-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
This chapter shows you how to use the <code>view-state</code> element to render views within a flow.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="view-convention">
|
||||
<title>Defining view states</title>
|
||||
<para>
|
||||
Use the <code>view-state</code> element to define a step of the flow that renders a view and waits for a user event to resume:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails">
|
||||
<transition on="submit" to="reviewBooking" />
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
By convention, a view-state maps its id to a view template in the directory where the flow is located.
|
||||
For example, the state above might render <filename>/WEB-INF/hotels/booking/enterBookingDetails.xhtml</filename>
|
||||
if the flow itself was located in the <filename>/WEB-INF/hotels/booking</filename> directory.
|
||||
</para>
|
||||
<para>
|
||||
Below is a sample directory structure showing views and other resources like message bundles co-located with their flow definition:
|
||||
</para>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/flow-view-packaging.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/flow-view-packaging.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
<caption>
|
||||
<para>Flow Packaging</para>
|
||||
</caption>
|
||||
</mediaobject>
|
||||
</sect1>
|
||||
<sect1 id="view-explicit">
|
||||
<title>Specifying view identifiers</title>
|
||||
<para>
|
||||
Use the <code>view</code> attribute to specify the id of the view to render explicitly.
|
||||
</para>
|
||||
<sect2 id="view-explicit-flowrelative">
|
||||
<title>Flow relative view ids</title>
|
||||
<para>
|
||||
The view id may be a relative path to view resource in the flow's working directory:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="view-explicit-absolute">
|
||||
<title>Absolute view ids</title>
|
||||
<para>
|
||||
The view id may be a absolute path to a view resource in the webapp root directory:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml">]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="view-explicit-logical">
|
||||
<title>Logical view ids</title>
|
||||
<para>
|
||||
With some view frameworks, such as Spring MVC's view framework, the view id may also be a logical identifier resolved by the framework:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" view="bookingDetails">]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
See the Spring MVC integration section for more information on how to integrate with the MVC <code>ViewResolver</code> infrastructure.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-scope">
|
||||
<title>View scope</title>
|
||||
<para>
|
||||
A view-state allocates a new <code>viewScope</code> when it enters.
|
||||
This scope may be referenced within the view-state to assign variables that should live for the duration of the state.
|
||||
This scope is useful for manipulating objects over a series of requests from the same view, often Ajax requests.
|
||||
A view-state destroys its viewScope when it exits.
|
||||
</para>
|
||||
<sect2 id="view-scope-var">
|
||||
<title>Allocating view variables</title>
|
||||
<para>
|
||||
Use the <code>var</code> tag to declare a view variable.
|
||||
Like a flow variable, any <code>@Autowired</code> references are automatically restored when the view state resumes.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="view-scope-actions">
|
||||
<title>Assigning a viewScope variable</title>
|
||||
<para>
|
||||
Use the <code>on-render</code> tag to assign a variable from an action result before the view renders:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<on-render>
|
||||
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
|
||||
</on-render>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="view-scope-ajax">
|
||||
<title>Manipulating objects in view scope</title>
|
||||
<para>
|
||||
Objects in view scope are often manipulated over a series of requests from the same view.
|
||||
The following example pages through a search results list.
|
||||
The list is updated in view scope before each render.
|
||||
Asynchronous event handlers modify the current data page, then request re-rendering of the search results fragment.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="searchResults">
|
||||
<on-render>
|
||||
<evaluate expression="bookingService.findHotels(searchCriteria)"
|
||||
result="viewScope.hotels" />
|
||||
</on-render>
|
||||
<transition on="next">
|
||||
<evaluate expression="searchCriteria.nextPage()" />
|
||||
<render fragments="searchResultsFragment" />
|
||||
</transition>
|
||||
<transition on="previous">
|
||||
<evaluate expression="searchCriteria.previousPage()" />
|
||||
<render fragments="searchResultsFragment" />
|
||||
</transition>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-on-render">
|
||||
<title>Executing render actions</title>
|
||||
<para>
|
||||
Use the <code>on-render</code> element to execute one or more actions before view rendering.
|
||||
Render actions are executed on the initial render as well as any subsequent refreshes, including any partial re-renderings of the view.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<on-render>
|
||||
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
|
||||
</on-render>]]>
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="view-model">
|
||||
<title>Binding to a model</title>
|
||||
<para>
|
||||
Use the <code>model</code> attribute to declare a model object the view binds to.
|
||||
This attribute is typically used in conjunction with views that render data controls, such as forms.
|
||||
It enables form data binding and validation behaviors to be driven from metadata on your model object.
|
||||
</para>
|
||||
<para>
|
||||
The following example declares an <code>enterBookingDetails</code> state manipulates the <code>booking</code> model:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
The model may be an object in any accessible scope, such as <code>flowScope</code> or <code>viewScope</code>.
|
||||
Specifying a <code>model</code> triggers the following behavior when a view event occurs:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem><para>View-to-model binding. On view postback, user input values are bound to model object properties for you.</para></listitem>
|
||||
<listitem><para>Model validation. After binding, if the model object requires validation that validation logic will be invoked.</para></listitem>
|
||||
</orderedlist>
|
||||
<para>
|
||||
For a flow event to be generated that can drive a view state transition, model binding must complete successfully.
|
||||
If model binding fails, the view is re-rendered to allow the user to revise their edits.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="view-type-conversion">
|
||||
<title>Performing type conversion</title>
|
||||
<para>
|
||||
When request parameters are used to populate the model (commonly referred to as data binding), type conversion is required to parse String-based request parameter values before setting target model properties.
|
||||
Default type conversion is available for many common Java types such as numbers, primitives, enums, and Dates.
|
||||
Users also have the ability to register their own type conversion logic for user-defined types, and to override the default Converters.
|
||||
</para>
|
||||
<sect2 id="converter-options">
|
||||
<title>Type Conversion Options</title>
|
||||
<para>
|
||||
Starting with version 2.1 Spring Web Flow uses the <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert">type conversion</ulink> and <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#format">formatting</ulink> system introduced in Spring 3 for nearly all type conversion needs.
|
||||
Previously Web Flow applications used a type conversion mechanism that was different from the one in Spring MVC, which relied on the <code>java.beans.PropertyEditor</code> abstraction.
|
||||
Spring 3 offers a modern type conversion alternative to PropertyEditors that was actually influenced by Web Flow's own type conversion system.
|
||||
Hence Web Flow users should find it natural to work with the new Spring 3 type conversion.
|
||||
Another obvious and very important benefit of this change is that a single type conversion mechanism can now be used across Spring MVC And Spring Web Flow.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="converter-upgrade-to-spring-3">
|
||||
<title>Upgrading to Spring 3 Type Conversion And Formatting</title>
|
||||
<para>
|
||||
What does this practically mean for existing applications?
|
||||
Existing applications are likely registering their own converters of type <code>org.springframework.binding.convert.converters.Converter</code> through a sub-class of <code>DefaultConversionService</code> available in Spring Binding.
|
||||
Those converters can continue to be registered as before.
|
||||
They will be adapted as Spring 3 <code>GenericConverter</code> types and registered with a Spring 3 <code>org.springframework.core.convert.ConversionService</code> instance.
|
||||
In other words existing converters will be invoked through Spring's type conversion service.
|
||||
</para>
|
||||
<para>
|
||||
The only exception to this rule are named converters, which can be referenced from a <code>binding</code> element in a <code>view-state</code>:
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class ApplicationConversionService extends DefaultConversionService {
|
||||
public ApplicationConversionService() {
|
||||
addDefaultConverters();
|
||||
addDefaultAliases();
|
||||
addConverter("customConverter", new CustomConverter());
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<binder>
|
||||
<binding property="checkinDate" required="true" converter="customConverter" />
|
||||
</binder>
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
Named converters are not supported and cannot be used with the type conversion service available in Spring 3.
|
||||
Therefore such converters will not be adapted and will continue to work as before, i.e. will not involve the Spring 3 type conversion.
|
||||
However, this mechanism is deprecated and applications are encouraged to favor Spring 3 type conversion and formatting features.
|
||||
</para>
|
||||
<para>
|
||||
Also note that the existing Spring Binding <code>DefaultConversionService</code> no longer registers any default converters.
|
||||
Instead Web Flow now relies on the default type converters and formatters in Spring 3.
|
||||
</para>
|
||||
<para>
|
||||
In summary the Spring 3 type conversion and formatting is now used almost exclusively in Web Flow.
|
||||
Although existing applications will work without any changes, we encourage moving towards unifying the type conversion needs of Spring MVC and Spring Web Flow parts of applications.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="converter-configuration">
|
||||
<title>Configuring Type Conversion and Formatting</title>
|
||||
<para>
|
||||
In Spring MVC an instance of a <code>FormattingConversionService</code> is created automatically through the custom MVC namespace:
|
||||
<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:mvc="http://www.springframework.org/schema/mvc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/mvc
|
||||
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<mvc:annotation-driven/>
|
||||
]]>
|
||||
</programlisting>
|
||||
Internally that is done with the help of <code>FormattingConversionServiceFactoryBean</code>, which registers a default set of converters and formatters.
|
||||
You can customize the conversion service instance used in Spring MVC through the <code>conversion-service</code> attribute:
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<mvc:annotation-driven conversion-service="applicationConversionService" />]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
In Web Flow an instance of a Spring Binding <code>DefaultConversionService</code> is created automatically, which does not register any converters.
|
||||
Instead it delegates to a <code>FormattingConversionService</code> instance for all type conversion needs.
|
||||
By default this is not the same <code>FormattingConversionService</code> instance as the one used in Spring 3.
|
||||
However that won't make a practical difference until you start registering your own formatters.
|
||||
</para>
|
||||
<para>
|
||||
The <code>DefaultConversionService</code> used in Web Flow can be customized through the flow-builder-services element:
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<webflow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" />]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Connecting the dots in order to register your own formatters for use in both Spring MVC and in Spring Web Flow you can do the following.
|
||||
Create a class to register your custom formatters:
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class ApplicationConversionServiceFactoryBean extends FormattingConversionServiceFactoryBean {
|
||||
|
||||
@Override
|
||||
protected void installFormatters(FormatterRegistry registry) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
Configure it for use in Spring MVC:
|
||||
<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:mvc="http://www.springframework.org/schema/mvc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/mvc
|
||||
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<mvc:annotation-driven conversion-service="applicationConversionService" />
|
||||
|
||||
<!--
|
||||
Alternatively if you prefer annotations for DI:
|
||||
1. Add @Component to the factory bean.
|
||||
2. Add a component-scan element (from the context custom namespace) here.
|
||||
3. Remove XML bean declaration below.
|
||||
-->
|
||||
|
||||
<bean id="applicationConversionService" class="somepackage.ApplicationConversionServiceFactoryBean">
|
||||
|
||||
]]>
|
||||
</programlisting>
|
||||
Connection the Web Flow <code>DefaultConversionService</code> to the same "applicationConversionService" bean used in Spring MVC:
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" ... />
|
||||
|
||||
<webflow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" ... />
|
||||
|
||||
<bean id="defaultConversionService" class="org.springframework.binding.convert.service.DefaultConversionService">
|
||||
<constructor-arg ref="applicationConversionSevice"/>
|
||||
</bean>]]>
|
||||
</programlisting>
|
||||
Of course it is also possible to mix and match.
|
||||
Register new Spring 3 <code>Formatter</code> types through the "applicationConversionService".
|
||||
Register existing Spring Binding <code>Converter</code> types through the "defaultConversionService".
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="converter-working-with">
|
||||
<title>Working With Spring 3 Type Conversion And Formatting</title>
|
||||
<para>
|
||||
An important concept to understand is the difference between type converters and formatters.
|
||||
</para>
|
||||
<para>
|
||||
Type converters in Spring 3, provided in <code>org.springframework.core</code>, are for general-purpose type conversion between any two object types.
|
||||
In addition to the most simple <code>Converter</code> type, two other interfaces are <code>ConverterFactory</code> and <code>GenericConverter</code>.
|
||||
</para>
|
||||
<para>
|
||||
Formatters in Spring 3, provided in <code>org.springframework.context</code>, have the more specialized purpose of representing Objects as Strings.
|
||||
The <code>Formatter</code> interface extends the <code>Printer</code> and <code>Parser</code> interfaces for converting an Object to a String and turning a String into an Object.
|
||||
</para>
|
||||
<para>
|
||||
Web developers will find the <code>Formatter</code> interface most relevant because it fits the needs of web applications for type conversion.
|
||||
<note>
|
||||
<para>
|
||||
An important point to be made is that Object-to-Object conversion is a generalization of the more specific Object-to-String conversion.
|
||||
In fact in the end <code>Formatters</code> are reigstered as <code>GenericConverter</code> types with Spring's <code>GenericConversionService</code> making them equal to any other converter.
|
||||
</para>
|
||||
</note>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="converter-formatting-annotations">
|
||||
<title>Formatting Annotations</title>
|
||||
<para>
|
||||
One of the best features of the new type conversion is the ability to use annotations for a better control over formatting in a concise manner.
|
||||
Annotations can be placed on model attributes and on arguments of @Controller methods that are mapped to requests.
|
||||
Out of the box Spring provides two annotations <code>NumberFormat</code> and <code>DateTimeFormat</code> but you can create your own and have them registered along with the associated formatting logic.
|
||||
You can see examples of the <code>DateTimeFormat</code> annotation in the <ulink url="https://src.springframework.org/svn/spring-samples/travel">Spring Travel</ulink> and in the <ulink url="https://src.springframework.org/svn/spring-samples/petcare">Petcare</ulink> along with other samples in the <ulink url="https://src.springframework.org/svn/spring-samples">Spring Samples</ulink> repository.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="converter-dates">
|
||||
<title>Working With Dates</title>
|
||||
<para>
|
||||
The <code>DateTimeFormat</code> annotation implies use of <ulink url="http://joda-time.sourceforge.net/">Joda Time</ulink>.
|
||||
If that is present on the classpath the use of this annotation is enabled automatically.
|
||||
By default neither Spring MVC nor Web Flow register any other date formatters or converters.
|
||||
Therefore it is important for applications to register a custom formatter to specify the default way for printing and parsing dates.
|
||||
The <code>DateTimeFormat</code> annotation on the other hand provides more fine-grained control where it is necessary to deviate from the default.
|
||||
</para>
|
||||
<para>
|
||||
For more information on working with Spring 3 type conversion and formatting please refer to the relevant sections of the <ulink url="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/index.html">Spring documentation</ulink>.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-bind">
|
||||
<title>Suppressing binding</title>
|
||||
<para>
|
||||
Use the <code>bind</code> attribute to suppress model binding and validation for particular view events.
|
||||
The following example suppresses binding when the <code>cancel</code> event occurs:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<transition on="proceed" to="reviewBooking">
|
||||
<transition on="cancel" to="bookingCancelled" bind="false" />
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</sect1>
|
||||
<sect1 id="view-binder">
|
||||
<title>Specifying bindings explicitly</title>
|
||||
<para>
|
||||
Use the <code>binder</code> element to configure the exact set of model bindings usable by the view.
|
||||
This is particularly useful in a Spring MVC environment for restricting the set of "allowed fields" per view.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<binder>
|
||||
<binding property="creditCard" />
|
||||
<binding property="creditCardName" />
|
||||
<binding property="creditCardExpiryMonth" />
|
||||
<binding property="creditCardExpiryYear" />
|
||||
</binder>
|
||||
<transition on="proceed" to="reviewBooking" />
|
||||
<transition on="cancel" to="cancel" bind="false" />
|
||||
</view-state>
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
If the binder element is not specified, all public properties of the model are eligible for binding by the view.
|
||||
With the binder element specified, only the explicitly configured bindings are allowed.
|
||||
</para>
|
||||
<para>
|
||||
Each binding may also apply a converter to format the model property value for display in a custom manner.
|
||||
If no converter is specified, the default converter for the model property's type will be used.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<binder>
|
||||
<binding property="checkinDate" converter="shortDate" />
|
||||
<binding property="checkoutDate" converter="shortDate" />
|
||||
<binding property="creditCard" />
|
||||
<binding property="creditCardName" />
|
||||
<binding property="creditCardExpiryMonth" />
|
||||
<binding property="creditCardExpiryYear" />
|
||||
</binder>
|
||||
<transition on="proceed" to="reviewBooking" />
|
||||
<transition on="cancel" to="cancel" bind="false" />
|
||||
</view-state>
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the example above, the <code>shortDate</code> converter is bound to the
|
||||
<code>checkinDate</code> and <code>checkoutDate</code> properties.
|
||||
Custom converters may be registered with the application's ConversionService.
|
||||
</para>
|
||||
<para>
|
||||
Each binding may also apply a required check that will generate a validation error
|
||||
if the user provided value is null on form postback:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<binder>
|
||||
<binding property="checkinDate" converter="shortDate" required="true" />
|
||||
<binding property="checkoutDate" converter="shortDate" required="true" />
|
||||
<binding property="creditCard" required="true" />
|
||||
<binding property="creditCardName" required="true" />
|
||||
<binding property="creditCardExpiryMonth" required="true" />
|
||||
<binding property="creditCardExpiryYear" required="true" />
|
||||
</binder>
|
||||
<transition on="proceed" to="reviewBooking">
|
||||
<transition on="cancel" to="bookingCancelled" bind="false" />
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the example above, all of the bindings are required.
|
||||
If one or more blank input values are bound, validation errors will be generated and the view will re-render with those errors.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="view-validate">
|
||||
<title>Validating a model</title>
|
||||
<para>
|
||||
Model validation is driven by constraints specified against a model object.
|
||||
Web Flow supports enforcing such constraints programatically as well as
|
||||
declaratively with JSR-303 Bean Validation annotations.
|
||||
</para>
|
||||
<sect2 id="view-validation-jsr303">
|
||||
<title>JSR-303 Bean Validation</title>
|
||||
<para>
|
||||
Web Flow provides built-in support for the JSR-303 Bean Validation API
|
||||
building on equivalent support available in Spring MVC.
|
||||
To enable JSR-303 validation configure the flow-builder-services with
|
||||
Spring MVC's <code>LocalValidatorFactoryBean</code>:
|
||||
</para>
|
||||
<programlisting language="xml">
|
||||
<webflow:flow-registry flow-builder-services="flowBuilderServices" />
|
||||
|
||||
<webflow:flow-builder-services id="flowBuilderServices" validator="validator" />
|
||||
|
||||
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
With the above in place, the configured validator will be applied to
|
||||
all model attributes after data binding.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that JSR-303 bean validation and validation by convention
|
||||
(explained in the next section) are not mutually exclusive.
|
||||
In other words Web Flow will apply all available validation
|
||||
mechanisms.
|
||||
</para>
|
||||
|
||||
</sect2>
|
||||
<sect2 id="view-validation-programmatic">
|
||||
<title>Programmatic validation</title>
|
||||
<para>
|
||||
There are two ways to perform model validation programatically.
|
||||
The first is to implement validation logic in your model object.
|
||||
The second is to implement an external <code>Validator</code>.
|
||||
Both ways provide you with a <code>ValidationContext</code> to record error messages and access information about the current user.
|
||||
</para>
|
||||
<sect3 id="view-validation-programmatic-validate-method">
|
||||
<title>Implementing a model validate method</title>
|
||||
<para>
|
||||
Defining validation logic in your model object is the simplest way to validate its state.
|
||||
Once such logic is structured according to Web Flow conventions, Web Flow will automatically invoke that logic during the view-state postback lifecycle.
|
||||
Web Flow conventions have you structure model validation logic by view-state, allowing you to easily validate the subset of model properties that are editable on that view.
|
||||
To do this, simply create a public method with the name <code>validate${state}</code>, where <code>${state}</code> is the id of your view-state where you want validation to run.
|
||||
For example:
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class Booking {
|
||||
private Date checkinDate;
|
||||
private Date checkoutDate;
|
||||
...
|
||||
|
||||
public void validateEnterBookingDetails(ValidationContext context) {
|
||||
MessageContext messages = context.getMessageContext();
|
||||
if (checkinDate.before(today())) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkinDate").
|
||||
defaultText("Check in date must be a future date").build());
|
||||
} else if (!checkinDate.before(checkoutDate)) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
|
||||
defaultText("Check out date must be later than check in date").build());
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
In the example above, when a transition is triggered in a <code>enterBookingDetails</code> view-state that is editing a <code>Booking</code> model,
|
||||
Web Flow will invoke the <code>validateEnterBookingDetails(ValidationContext)</code> method automatically unless validation has been suppressed for that transition.
|
||||
An example of such a view-state is shown below:
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="enterBookingDetails" model="booking">
|
||||
<transition on="proceed" to="reviewBooking">
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Any number of validation methods are defined. Generally, a flow edits a model over a series of views. In that case, a validate method would be defined
|
||||
for each view-state where validation needs to run.
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="view-validation-programmatic-validator">
|
||||
<title>Implementing a Validator</title>
|
||||
<para>
|
||||
The second way is to define a separate object, called a <emphasis>Validator</emphasis>, which validates your model object.
|
||||
To do this, first create a class whose name has the pattern ${model}Validator, where <code>${model}</code> is the capitialized form of the model expression, such as <code>booking</code>.
|
||||
Then define a public method with the name <code>validate${state}</code>, where <code>${state}</code> is the id of your view-state, such as <code>enterBookingDetails</code>.
|
||||
The class should then be deployed as a Spring bean. Any number of validation methods can be defined.
|
||||
For example:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@Component
|
||||
public class BookingValidator {
|
||||
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
|
||||
MessageContext messages = context.getMessageContext();
|
||||
if (booking.getCheckinDate().before(today())) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkinDate").
|
||||
defaultText("Check in date must be a future date").build());
|
||||
} else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
|
||||
messages.addMessage(new MessageBuilder().error().source("checkoutDate").
|
||||
defaultText("Check out date must be later than check in date").build());
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the example above, when a transition is triggered in a <code>enterBookingDetails</code> view-state that is editing a <code>Booking</code> model,
|
||||
Web Flow will invoke the <code>validateEnterBookingDetails(Booking, ValidationContext)</code> method automatically unless validation has been suppressed for that transition.
|
||||
</para>
|
||||
<para>
|
||||
A Validator can also accept a Spring MVC <code>Errors</code> object, which is required for invoking existing Spring Validators.
|
||||
</para>
|
||||
<para>
|
||||
Validators must be registered as Spring beans employing the naming convention <code>${model}Validator</code> to be detected and invoked automatically.
|
||||
In the example above, Spring 2.5 classpath-scanning would detect the <code>@Component</code> and automatically register it as a bean with the name <code>bookingValidator</code>.
|
||||
Then, anytime the <code>booking</code> model needs to be validated, this <code>bookingValidator</code> instance would be invoked for you.
|
||||
</para>
|
||||
</sect3>
|
||||
<sect3 id="default-validate-method">
|
||||
<title>Default validate method</title>
|
||||
<para>
|
||||
A <emphasis>Validator</emphasis> class can also define a method called <code>validate</code> not associated (by convention) with any specific view-state.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@Component
|
||||
public class BookingValidator {
|
||||
public void validate(Booking booking, ValidationContext context) {
|
||||
//...
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In the above code sample the method <code>validate</code> will be called every time a Model of type <code>Booking</code> is validated (unless validation has been suppressed for that transition).
|
||||
If needed the default method can also be called in addition to an existing state-specific method. Consider the following example:
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
@Component
|
||||
public class BookingValidator {
|
||||
public void validate(Booking booking, ValidationContext context) {
|
||||
//...
|
||||
}
|
||||
public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
|
||||
//...
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In above code sample the method <code>validateEnterBookingDetails</code> will be called first.
|
||||
The default <code>validate</code> method will be called next.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2 id="view-validation-context">
|
||||
<title>ValidationContext</title>
|
||||
<para>
|
||||
A ValidationContext allows you to obtain a <code>MessageContext</code> to record messages during validation.
|
||||
It also exposes information about the current user, such as the signaled <code>userEvent</code> and the current user's <code>Principal</code> identity.
|
||||
This information can be used to customize validation logic based on what button or link was activated in the UI, or who is authenticated.
|
||||
See the API Javadocs for <code>ValidationContext</code> for more information.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-validation-suppression">
|
||||
<title>Suppressing validation</title>
|
||||
<para>
|
||||
Use the <code>validate</code> attribute to suppress model validation for particular view events:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="chooseAmenities" model="booking">
|
||||
<transition on="proceed" to="reviewBooking">
|
||||
<transition on="back" to="enterBookingDetails" validate="false" />
|
||||
</view-state>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
In this example, data binding will still occur on <code>back</code> but validation will be suppressed.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="view-transitions">
|
||||
<title>Executing view transitions</title>
|
||||
<para>
|
||||
Define one or more <code>transition</code> elements to handle user events that may occur on the view.
|
||||
A transition may take the user to another view, or it may simply execute an action and re-render the current view.
|
||||
A transition may also request the rendering of parts of a view called "fragments" when handling an Ajax event.
|
||||
Finally, "global" transitions that are shared across all views may also be defined.
|
||||
</para>
|
||||
<para>
|
||||
Implementing view transitions is illustrated in the following sections.
|
||||
</para>
|
||||
<sect2 id="transition-actions">
|
||||
<title>Transition actions</title>
|
||||
<para>
|
||||
A view-state transition can execute one or more actions before executing.
|
||||
These actions may return an error result to prevent the transition from exiting the current view-state.
|
||||
If an error result occurs, the view will re-render and should display an appropriate message to the user.
|
||||
</para>
|
||||
<para>
|
||||
If the transition action invokes a plain Java method, the invoked method may return false to prevent the transition from executing.
|
||||
This technique can be used to handle exceptions thrown by service-layer methods.
|
||||
The example below invokes an action that calls a service and handles an exceptional situation:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<transition on="submit" to="bookingConfirmed">
|
||||
<evaluate expression="bookingAction.makeBooking(booking, messageContext)" />
|
||||
</transition>]]>
|
||||
</programlisting>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public class BookingAction {
|
||||
public boolean makeBooking(Booking booking, MessageContext context) {
|
||||
try {
|
||||
bookingService.make(booking);
|
||||
return true;
|
||||
} catch (RoomNotAvailableException e) {
|
||||
context.addMessage(new MessageBuilder().error().
|
||||
.defaultText("No room is available at this hotel").build());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}]]>
|
||||
</programlisting>
|
||||
<note>
|
||||
<para>
|
||||
When there is more than one action defined on a transition, if one returns an error result the remaining actions in the set will <emphasis>not</emphasis> be executed.
|
||||
If you need to ensure one transition action's result cannot impact the execution of another, define a single transition action that invokes a method that encapsulates all the action logic.
|
||||
</para>
|
||||
</note>
|
||||
</sect2>
|
||||
<sect2 id="event-handlers-global">
|
||||
<title>Global transitions</title>
|
||||
<para>
|
||||
Use the flow's <code>global-transitions</code> element to create transitions that apply across all views.
|
||||
Global-transitions are often used to handle global menu links that are part of the layout.
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<global-transitions>
|
||||
<transition on="login" to="login" />
|
||||
<transition on="logout" to="logout" />
|
||||
</global-transitions>]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="simple-event-handlers">
|
||||
<title>Event handlers</title>
|
||||
<para>
|
||||
From a view-state, transitions without targets can also be defined. Such transitions are called "event handlers":
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<transition on="event">
|
||||
<!-- Handle event -->
|
||||
</transition>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
These event handlers do not change the state of the flow.
|
||||
They simply execute their actions and re-render the current view or one or more fragments of the current view.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2 id="event-handlers-render">
|
||||
<title>Rendering fragments</title>
|
||||
<para>
|
||||
Use the <code>render</code> element within a transition to request partial re-rendering of the current view after handling the event:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<transition on="next">
|
||||
<evaluate expression="searchCriteria.nextPage()" />
|
||||
<render fragments="searchResultsFragment" />
|
||||
</transition>]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
The fragments attribute should reference the id(s) of the view element(s) you wish to re-render.
|
||||
Specify multiple elements to re-render by separating them with a comma delimiter.
|
||||
</para>
|
||||
<para>
|
||||
Such partial rendering is often used with events signaled by Ajax to update a specific zone of the view.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-messages">
|
||||
<title>Working with messages</title>
|
||||
<para>
|
||||
Spring Web Flow's <code>MessageContext</code> is an API for recording messages during the course of flow executions.
|
||||
Plain text messages can be added to the context, as well as internationalized messages resolved by a Spring <code>MessageSource</code>.
|
||||
Messages are renderable by views and automatically survive flow execution redirects.
|
||||
Three distinct message severities are provided: <code>info</code>, <code>warning</code>, and <code>error</code>.
|
||||
In addition, a convenient <code>MessageBuilder</code> exists for fluently constructing messages.
|
||||
</para>
|
||||
<sect2 id="plain-text-message">
|
||||
<title>Adding plain text messages</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
MessageContext context = ...
|
||||
MessageBuilder builder = new MessageBuilder();
|
||||
context.addMessage(builder.error().source("checkinDate")
|
||||
.defaultText("Check in date must be a future date").build());
|
||||
context.addMessage(builder.warn().source("smoking")
|
||||
.defaultText("Smoking is bad for your health").build());
|
||||
context.addMessage(builder.info()
|
||||
.defaultText("We have processed your reservation - thank you and enjoy your stay").build());]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="plain-text-message-intl">
|
||||
<title>Adding internationalized messages</title>
|
||||
<programlisting language="java"><![CDATA[
|
||||
MessageContext context = ...
|
||||
MessageBuilder builder = new MessageBuilder();
|
||||
context.addMessage(builder.error().source("checkinDate").code("checkinDate.notFuture").build());
|
||||
context.addMessage(builder.warn().source("smoking").code("notHealthy")
|
||||
.resolvableArg("smoking").build());
|
||||
context.addMessage(builder.info().code("reservationConfirmation").build());]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="message-bundles">
|
||||
<title>Using message bundles</title>
|
||||
<para>
|
||||
Internationalized messages are defined in message bundles accessed by a Spring <code>MessageSource</code>.
|
||||
To create a flow-specific message bundle, simply define <code>messages.properties</code> file(s) in your flow's directory.
|
||||
Create a default <code>messages.properties</code> file and a .properties file for each additional <code>Locale</code> you need to support.
|
||||
</para>
|
||||
<programlisting><![CDATA[
|
||||
#messages.properties
|
||||
checkinDate=Check in date must be a future date
|
||||
notHealthy={0} is bad for your health
|
||||
reservationConfirmation=We have processed your reservation - thank you and enjoy your stay]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
From within a view or a flow, you may also access message resources using the <code>resourceBundle</code> EL variable:
|
||||
</para>
|
||||
<programlisting><![CDATA[
|
||||
<h:outputText value="#{resourceBundle.reservationConfirmation}" />]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="message-generation">
|
||||
<title>Understanding system generated messages</title>
|
||||
<para>
|
||||
There are several places where Web Flow itself will generate messages to display to the user.
|
||||
One important place this occurs is during view-to-model data binding.
|
||||
When a binding error occurs, such as a type conversion error, Web Flow will map that error to a message retrieved from your resource bundle automatically.
|
||||
To lookup the message to display, Web Flow tries resource keys that contain the binding error code and target property name.
|
||||
</para>
|
||||
<para>
|
||||
As an example, consider a binding to a <code>checkinDate</code> property of a <code>Booking</code> object.
|
||||
Suppose the user typed in a alphabetic string.
|
||||
In this case, a type conversion error will be raised.
|
||||
Web Flow will map the 'typeMismatch' error code to a message by first querying your resource bundle for a message with the following key:
|
||||
</para>
|
||||
<programlisting>
|
||||
booking.checkinDate.typeMismatch
|
||||
</programlisting>
|
||||
<para>
|
||||
The first part of the key is the model class's short name.
|
||||
The second part of the key is the property name. The third part is the error code.
|
||||
This allows for the lookup of a unique message to display to the user when a binding fails on a model property.
|
||||
Such a message might say:
|
||||
</para>
|
||||
<programlisting>
|
||||
booking.checkinDate.typeMismatch=The check in date must be in the format yyyy-mm-dd.
|
||||
</programlisting>
|
||||
<para>
|
||||
If no such resource key can be found of that form, a more generic key will be tried.
|
||||
This key is simply the error code. The field name of the property is provided as a message argument.
|
||||
</para>
|
||||
<programlisting>
|
||||
typeMismatch=The {0} field is of the wrong type.
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="view-popup">
|
||||
<title>Displaying popups</title>
|
||||
<para>
|
||||
Use the <code>popup</code> attribute to render a view in a modal popup dialog:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
When using Web Flow with the Spring Javascript, no client side code is necessary for the popup to display.
|
||||
Web Flow will send a response to the client requesting a redirect to the view from a popup, and the client will honor the request.
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="view-backtracking">
|
||||
<title>View backtracking</title>
|
||||
<para>
|
||||
By default, when you exit a view state and transition to a new view state, you can go back to the previous state using the browser back button.
|
||||
These view state history policies are configurable on a per-transition basis by using the <code>history</code> attribute.
|
||||
</para>
|
||||
<sect2 id="history-discard">
|
||||
<title>Discarding history</title>
|
||||
<para>
|
||||
Set the history attribute to <code>discard</code> to prevent backtracking to a view:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<transition on="cancel" to="bookingCancelled" history="discard">]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
<sect2 id="history-invalidate">
|
||||
<title>Invalidating history</title>
|
||||
<para>
|
||||
Set the history attribute to <code>invalidate</code> to prevent backtracking to a view as well all previously displayed views:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[
|
||||
<transition on="confirm" to="bookingConfirmed" history="invalidate">]]>
|
||||
</programlisting>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user