Multi-action that implements common logic dealing with input forms.
Several action execution methods are provided:
setupForm(RequestContext) - Prepares the backing form object
for display on a form. This method behaves exactly like exposeFormObject but
goes further by adding a capability to perform optional data binding on
setup. This action method will return the success() event if there are no
setup errors, otherwise it will return the error() event.
bindAndValidate(RequestContext) - Binds all incoming event
parameters to the form object and validates the form object using a
registered validator. This action method will return the success() event if
there are no binding or validation errors, otherwise it will return the
error() event.
bind(RequestContext) - Binds all incoming event parameters to
the form object. No additional validation is performed. This action method
will return the success() event if there are no binding errors, otherwise it
will return the error() event.
validate(RequestContext) - Validates the form object using a
registered validator. No data binding is performed. This action method will
return the success() event if there are no validation errors, otherwise it
will return the error() event.
resetForm(RequestContext) - Resets the form by reloading the
backing form object and reinstalling any custom property editors. Returns
success() on completion, error() if a form object load failure occurs.
Since this is a multi-action, a subclass could add any number of additional
action execution methods, e.g. "setupReferenceData(RequestContext)", or
"processSubmit(RequestContext)".
Using this action, it becomes very easy to implement form preparation and
submission logic in your flow. One way to do this follows:
Create a view state to display the form. In an entry action of that
state, invoke setupForm to prepare the new
form for display.
On submit, execute a state transition action that performs a
bindAndValidate. This will invoke
bindAndValidate to bind incoming
event parameters to the form object and validate the form object.
If there are binding or validation errors, the transition will not be
allowed and the view state will automatically be re-entered.
If binding and validation is successful, go to an action state called
"processSubmit" (or any other appropriate name). This will invoke an action
method called "processSubmit" you must provide on a subclass to process form
submission, e.g. interacting with the business logic.
If business processing is ok, continue to a view state to display the
success view.
Here is an example implementation of such a compact form flow:
When you need additional flexibility, consider splitting the view state above
acting as a single logical form state into multiple states. For
example, you could have one action state handle form setup, a view state
trigger form display, another action state handle data binding and
validation, and another process form submission. This would be a bit more
verbose but would also give you more control over how you respond to specific
results of fine-grained actions that occur within the flow.
Subclassing hooks:
An option hook method provided by this class is
initBinder . This is called
after a new data binder is created by any of the action execution methods. It
allows you to install any custom property editors required to format
richly-typed form object property values. Note: consider setting an explicit
PropertyEditorRegistrar strategy as a more
reusable way to encapsulate custom PropertyEditor installation logic.
Another important hook is
loadFormObject . You may override
this to customize where the backing form object comes from (e.g instantiated
directly in memory or loaded from a database).
Note that this action does not provide a referenceData() hook method
similar to that of Spring MVC's SimpleFormController. If you
need to expose reference data to populate form drop downs for example, you
should create a custom action method in your FormAction subclass that does
just that, and invoke it as either a chained action as part of a form setup
state, or as a fine grained state definition itself.
For example, you might create this method in your subclass:
The scope in which the form object will be put. If put in flow scope the
object will be cached and reused over the life of the flow, preserving
previous values. Request scope will cause a new fresh form object instance to
be created on each request into the flow execution.
The scope in which the form object errors instance will be put. If put
in flow scope the errors will be cached and reused over the life of the flow.
Request scope will cause a new errors instance to be created each execution.
propertyEditorRegistrar
null
The strategy used to register custom property editors with the data
binder. This is an alternative to overriding the
initBinder hook method.
validator
null
The validator for this action. The validator must support the specified
form object class.
validateUsingValidatorMethod
false
Indicates if the validator should be invoked ONLY if the
VALIDATOR_METHOD_PROPERTY context property is set before this action
is executed. Useful for supporting piecemeal validation of a form object as
part of a wizard flow.
messageCodesResolver
null
Set the strategy to use for resolving errors into message codes.
Several action execution methods are provided:
Since this is a multi-action, a subclass could add any number of additional action execution methods, e.g. "setupReferenceData(RequestContext)", or "processSubmit(RequestContext)".
Using this action, it becomes very easy to implement form preparation and submission logic in your flow. One way to do this follows:
Here is an example implementation of such a compact form flow:
<view-state id="displayCriteria" view="searchCriteria"> <entry-actions> <action bean="searchFormAction" method="setupForm"/> </entry-actions> <transition on="search" to="executeSearch"> <action bean="searchFormAction" method="bindAndValidate"/> </transition> </view-state> <action-state id="executeSearch"> <action bean="searchFormAction"/> <transition on="success" to="displayResults"/> </action-state>When you need additional flexibility, consider splitting the view state above acting as a single logical form state into multiple states. For example, you could have one action state handle form setup, a view state trigger form display, another action state handle data binding and validation, and another process form submission. This would be a bit more verbose but would also give you more control over how you respond to specific results of fine-grained actions that occur within the flow.
Subclassing hooks:
Note that this action does not provide a referenceData() hook method similar to that of Spring MVC's
SimpleFormController. If you need to expose reference data to populate form drop downs for example, you should create a custom action method in your FormAction subclass that does just that, and invoke it as either a chained action as part of a form setup state, or as a fine grained state definition itself.For example, you might create this method in your subclass:
public Event setupReferenceData(RequestContext context) throws Exception { Scope requestScope = context.getRequestScope(); requestScope.setAttribute("refData", referenceDataDao.getSupportingFormData()); return success(); }FormAction configurable properties