Nested Page Flows
Introduction
By default, executing an action in a new page flow causes the current page flow to be discarded. This behavior allows you to create separate controllers for different sections of your project, and it minimizes the amount of data kept in the user session at one time. Each page flow manages its own state and logic. "Nested page flows" give you an even greater ability to break up your project into separate, self-contained bits of functionality. At its heart, "nesting" is a way of pushing aside the current page flow temporarily and transferring control to another page flow with the intention of coming back to the original one.
So when would you use this? Nesting is useful when you want to do one of the following tasks:
- gather data from the user, for use in the current page flow;
- allow the user to correct errors or supply additional information en route to executing a desired action;
- show an alternate view of data represented in the current page flow;
- bring the user through a "wizard";
- show the user information that will be useful in the current page flow (e.g., help screens can be easily implemented as nested page flows); and
- in general, to further break up your application into separate (and in many cases reusable) pieces.
Basics
Let's start with a very simple example. You can find the code for this under basicNesting in the NetUI Samples application. Here, we have a "main" page flow which forwards to a nested page flow, which later returns to the main page flow. The flow looks like this:
Here is the code for the main page flow:
@Jpf.Controller( simpleActions={ @Jpf.SimpleAction(name="begin", path="index.jsp"), @Jpf.SimpleAction(name="goNested", path="../nested/NestedFlow.jpf"), @Jpf.SimpleAction(name="nestedDone", path="success.jsp") } ) public class MainFlow extends PageFlowController { }
As you can see, the begin action forwards to index.jsp, which allows you to raise the goNested action. This action enters the nested page flow simply by forwarding to it. Any time you hit the URL for a nested page flow (or any one of its actions or pages), you enter the nested page flow, and the current one is pushed aside.
When the nested page flow returns, it causes the nestedDone action to run, and this action simply forwards to success.jsp
So how does the nested page flow return to the current one, and raise the nestedDone action? Here is the code for the nested flow:
@Jpf.Controller( nested=true, simpleActions={ @Jpf.SimpleAction(name="begin", path="index.jsp"), @Jpf.SimpleAction(name="done", returnAction="nestedDone") } ) public class NestedFlow extends PageFlowController { }
Note the nested =true, which defines this as a nested page flow. Also note the returnAction attribute on the simple action done. When this action is executed, it returns to the original page flow (MainFlow) and raises its nestedDone action. This is called an exit point of the nested page flow.
The nested flow looks like this:
More Nested Page Flow Features
Often, you want to do more than simply invoke and return from a nested page flow. For instance, you may want to gather data from a nested page flow, for use in the current page flow. In the example below (found under nesting in the NetUI Samples project), the user is forwarded to a nested page flow (/nesting/chooseAirport/chooseAirport.jpf), which is a wizard that helps the user find an airport. The nested page flow returns the chosen airport to the original page flow, which continues with its sequence. First, here is a diagram of the main page flow:
This page flow demonstrates two new features related to nesting:
- The nested page flow returns a form bean (ChooseAirport.Results) when it raises the chooseAirportDone action.
- If the nested page flow raises a chooseAirportCancelled action, the page flow will go back to the most recent page shown to the user., whatever that page is.
Returning data from a nested page flow
Here is a diagram of the nested page flow ChooseAirport.jpf, with the "happy path" to the chooseAirportDone return-action hightlighted in red:
During the course of this page flow, a member variable called _currentResults, of type ChooseAirport.Results is kept. As the user moves through the page flow, possibly re-trying and changing the desired result, this member variable is kept up-to-date. In the end, it is returned as an "output form bean" along with the chooseAirportDone return-action, using the outputFormBean attribute. This is what it looks like in the annotations:
@Jpf.SimpleAction(name="confirmResults", returnAction="chooseAirportDone", outputFormBean="_currentResults")
This annotation simply specifies that the value of _currentResults will be sent along with the return-action chooseAirportDone.
@Jpf.Action(
forwards={
@Jpf.Forward(name="done", returnAction="chooseAirportDone", outputFormBeanType=Results.class)
}
)
public Forward confirmResults()
{
Results results = initialize a Results object
return new Forward("done", results);
}
In the original page flow, there is a chooseAirportDone method that accepts this form bean as an argument, like this:
@Jpf.Action( ... ) protected Forward chooseAirportDone(ChooseAirport.Results results) { ... }
As you can see, the page flow handles this returned form bean just like it would handle a form bean posted from a page.
Navigating back to the original page of the main flow
The the current example, the main page flow goes back to the most recent page whenever the nested page flow raises the chooseAirportCancelled action. We are referring to this section of the diagram of the main page flow:
To navigate to the most recent page, the page flow uses the navigateTo attribute on a @Jpf.SimpleAction (or a @Jpf.Forward ), like this:
@Jpf.SimpleAction(name="chooseAirportCancelled", navigateTo=Jpf.NavigateTo.currentPage)
Or could also go back to the previous page:
@Jpf.SimpleAction(name="chooseAirportCancelled", navigateTo=Jpf.NavigateTo.previousPage)
Or it could re-run the most recent action in the current page flow:
@Jpf.SimpleAction(name="chooseAirportCancelled", navigateTo=Jpf.NavigateTo.previousAction)
Passing Data to a Nested Page Flow
Sometimes you will want to pass data into a nested page flow. While this may be less common than returning data from a nested page flow, it is useful for initializing data in the flow. To do this, you will add a form bean to the begin action of the nested page flow, and in the calling page flow you will pass an instance of this bean on the Forward object that is sent to the nested page flow.
Add a form bean to the nested page flow's begin action
To add a form bean, simply add a single argument to a begin action method:
@Jpf.Action( forwards={ @Jpf.Forward(name="index", path="index.jsp") } ) public Forward begin(InitBean initBean) { ... }
The bean type can be any class of your choosing; for instance, you can make it a String, which means that the nested page flow requires a String to be initialized:
@Jpf.Action( forwards={ @Jpf.Forward(name="index", path="index.jsp") } ) public Forward begin(String initString) { ... }
Pass the form bean from the calling page flow to the nested page flow
You can pass the initialization bean to the nested page flow by adding it to the Forward object that is sent to the nested page flow:
@Jpf.Action( forwards={ @Jpf.Forward(name="nestedFlow", path="/nested/NestedFlow.jpf", outputFormBeanType=InitBean.class) } ) public Forward goNested() { InitBean initBean = initialize the bean return new Forward("nestedFlow", initBean); }
Note that the outputFormBeanType annotation attribute is optional; it mainly helps tools understand the output of the action, and it ensures that an incompatible type will not be passed.
Other Notes
Here are some other notes about using nested page flows:
- Aside from the nested=true on the @Jpf.Controller annotation, and defining exit points through returnAction, nested page flows are built just like other non-nested page flows.
- You enter a nested page flow by hitting its URL, by hitting the URL for any of its actions, or by hitting the URL for any of its pages (pages in the same directory path). When nesting occurs, the original page flow is pushed onto the "nesting stack", and is popped off the stack when the nested page flow hits an exit point (through a returnAction attribute).
- Nested page flows can nest themselves.
- While in a nested page flow, you can get a reference to the calling page flow through PageFlowUtils.getNestingPageFlow().