656 lines
31 KiB
XML
656 lines
31 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter 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 a model binding occurs during view postback, the binding system will attempt to convert the input value to the type of the target model property if necessary.
|
|
Default Converters are registered for common types such as Numbers, primitives, enums, and Dates and are applied automatically.
|
|
Users also have the ability to register their own converters for user-defined types, and to override the default Converters.
|
|
</para>
|
|
<sect2 id="converter-impl">
|
|
<title>Implementing a Converter</title>
|
|
<para>
|
|
To implement your own Converter, implement the <code>org.springframework.binding.convert.converters.TwoWayConverter</code> interface.
|
|
A convenient <code>StringToObject</code> base class has been provided to simplify the implementation of this interface for converters
|
|
that convert from a user input String to a user-defined Object and back. Simply extend from this class and override these two methods:
|
|
</para>
|
|
<programlisting language="java">
|
|
protected abstract Object toObject(String string, Class targetClass) throws Exception;
|
|
|
|
protected abstract String toString(Object object) throws Exception;
|
|
</programlisting>
|
|
<para>
|
|
<code>toObject(String, Class)</code> should convert from the input string to your object's type, and <code>toString(Object)</code> should do the reverse.
|
|
</para>
|
|
<para>
|
|
The following example shows a Converter that converts from String to a MonetaryAmount for working with currency values:
|
|
</para>
|
|
<programlisting language="java">
|
|
public class StringToMonetaryAmount extends StringToObject {
|
|
|
|
public StringToMonetaryAmount() {
|
|
super(MonetaryAmount.class);
|
|
}
|
|
|
|
@Override
|
|
protected Object toObject(String string, Class targetClass) {
|
|
return MonetaryAmount.valueOf(string);
|
|
}
|
|
|
|
@Override
|
|
protected String toString(Object object) {
|
|
MonetaryAmount amount = (MonetaryAmount) object;
|
|
return amount.toString();
|
|
}
|
|
}
|
|
</programlisting>
|
|
<para>
|
|
Review the pre-built converters in the <code>org.springframework.binding.convert.converters</code> package to see more examples of Converter implementations.
|
|
</para>
|
|
</sect2>
|
|
<sect2 id="converter-registration">
|
|
<title>Registering a Converter</title>
|
|
<para>
|
|
To install your own Converter or override any of the default Converters, extend from <code>org.springframework.binding.convert.service.DefaultConversionService</code> and override the <code>addDefaultConverters()</code> method.
|
|
Use the <code>addConverter(Converter)</code> method to register the primary Converter to use to convert between two types, such as a <code>String</code> and a <code>MonetaryAmount</code>.
|
|
Optionally use the <code>addConverter(String, Converter)</code> method to register alternate converters for the same type pair; for example, to support formatting a <code>java.util.Date</code> as a String in several different ways.
|
|
</para>
|
|
<para>
|
|
Each alternate Converter is indexed by a unique <code>converterId</code> that can be referenced when configuring a model binding.
|
|
When no converter id is referenced explicitly by a binding, the primary Converter between the two types is always used.
|
|
</para>
|
|
<para>
|
|
The ConversionService is the object Web Flow consults at runtime to lookup conversion executors to convert from one type to another.
|
|
There is generally one ConversionService per application.
|
|
See the <link linkend="builder-service-conversion">System Setup</link> section for documentation on how to configure an extended ConversionService implementation that registers custom Converters to apply application-wide.
|
|
Also consult the Convert API documentation for more information.
|
|
</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.
|
|
</para>
|
|
<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:
|
|
</para>
|
|
<programlisting language="java"><![CDATA[
|
|
public class Booking {
|
|
private Date checkinDate;
|
|
private Date checkoutDate;
|
|
...
|
|
|
|
public void validateEnterBookingDetails(ValidationContext context) {
|
|
MessageContext messages = context.getMessages();
|
|
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>
|
|
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:
|
|
</para>
|
|
<programlisting language="xml"><![CDATA[
|
|
<view-state id="enterBookingDetails" model="booking">
|
|
<transition on="proceed" to="reviewBooking">
|
|
</view-state>]]>
|
|
</programlisting>
|
|
<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.getMessages();
|
|
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>
|
|
</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(builder.error().
|
|
.defaultText("No room is available at this hotel").build());
|
|
return false;
|
|
}
|
|
}
|
|
}]]>
|
|
</programlisting>
|
|
</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> |