I've been working with SWF for about 3 weeks now and I successfully ported a Spring MVC/JSP recipe entry form into 4 separate views, consisting of 1) basic info, 2) ingredients, 3) instructions and 4) optional info. I would like to enhance the flow so that #2 and/or #3 can be executed 1 to x times based upon a user entry on #1. An example would be a recipe that includes a set of ingredients for a cake and a separate set for the icing - the user would indicate there are 2 sets of ingredients on view #1 and view #2 would be displayed twice.
I've read through the docs and tons of posts and I'm still really confused as to the best way to approach this. In essence I need to set a variable in view #1 that webflow will evaluate and do a kind of for
loop on view #2.
Currently, the number of iterations is not part of the model used by the views, but it seems like most of the examples and posts I've looked at point to needing to add it to the model then do an <evaluate>
in the flow to determine the number of times to iterate view #2. Alternatively, is there a way to set a <var>
in the webflow.xml that can be updated in the view #1 jsp form and then evaluated in view #2 without calling a server function? Or is using <input>
and output
a possible solution? Or could I use a requestparam?
I have to admit being a bit flummoxed by this. Any help with pointing me to examples, docs, other posts that I somehow missed would be greatly appreciated.
Brief Explanation:
I think you're misunderstanding the intended purpose of a 'view-state' and Spring webflow in general.
Spring Webflow = An xml file that describes a "Sequential" logical flow of states that achieve a single goal (e.g 'Book an airline ticket', 'Gather information for cooking receipes', etc...)
"Sequential" here means that every single state (e.g view-state, action-state, decision-state, end-state) is a SINGLE UNIT of EXECUTION inside Spring webflow. Where each state EXPLICITLY defines what the 'next' state is going to be once it completes execution. For instance,
(view-state A) -> (action-state B) -> (decision-state C) -> (end-state D)
There is no concept of iteration defined with in each state because they are single units of execution. Once a state finishes execution it must go to the next state as defined by the 'transition' tag.
<view-state id="stateA">
<transition to="stateB"/>
</view-state>
Also notice I wrote "Sequential" in quotes because although the flow is sequential (from state -> state)... we can still jump back to any state we wish like so.
(view-state A) -> (action-state B) -> (decision-state C) -> (view-state A)
So...your interpretation of internal view-state iteration does not exist.
Answer to your question:
There are several ways to achieve the 'state' iteration you desire but it sounds like you need a decision-state to delegate the flow based on some flowScope variable. Basically you want to keep track of the num of times you want to execute a particular state then everytime you execute the state you decrement the variable by ( 1 ) and reevaluate it until the num of executions == 0.
Sample flow defined below with comments ( not based on the example code just from your description ).
<!-- define the 'global' flowScope variable that will track the num of times you want to execute the ingredients state -->
<set name="flowScope.numOfTimesToExecuteIngredientsState" value="0" type="java.lang.Integer"/>
<view-state id="basicInfoState" model="basicInfoModel">
<!-- initially goto the decision state and make sure to set the num of execution inside the transition to the 'global' flowScope tracking variable -->
<transition on="save" to="ingredientsIteratorDecisionState">
<!-- assuming ingredients is an initialized Set<Ingredients>... get the size and put inside a flowScope var -->
<set name="flowScope.numOfTimesToExecuteIngredientsState" value="basicInfoModel.getIngredients().size()"/>
</transition>
</view-state>
<decision-state id="ingredientsIteratorDecisionState">
<!-- if # of executions is more than 0 go back to ingredientState otherwise move on to instructionsState -->
<if test="flowScope.numOfTimesToExecuteIngredientsState > 0" then="ingredientsState" else="instructionsState"/>
</decision-state>
<view-state id="ingredientsState">
<transition on="save" to="ingredientsIteratorDecisionState">
<! -- upon a successful save... decrement numOfIngredientsSelected by ( 1 ) -->
<set name="flowScope.numOfTimesToExecuteIngredientsState" value="flowScope.numOfTimesToExecuteIngredientsState - 1"/>
</transition>
</view-state>
Disclaimer: the flow definitions above were not tested. I just wrote it from memory. There might be syntax errors.
You also asked:
Alternatively, is there a way to set a in the webflow.xml that can be updated in the view #1 jsp form and then evaluated in view #2 without calling a server function? Or is using and output a possible solution? Or could I use a requestparam?
No. There will always be a call to the server. Why not just deduce the # of recipes from the collection object's size stored on your model?
You could enable SWF the handling of ajax requests and send a requestParam to a transition with no defined 'to=' state. (meaning it will stay in the current view-state) and inside the transition tag do your custom logic (see: How to include a pop-up dialog box in subflow)
but I think this is a design problem. All the values you pass should be in a single http request (and therefore part of your model).