Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3)
B25386-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

11.5 Creating a Multipage Process

If you have a set of pages that should be visited in a particular order, consider using the processTrain and processChoiceBar components to show the multipage process. In the SRDemo application, the SRCreate.jspx and SRCreateConfirm.jspx pages use a processTrain and processChoiceBar component to let a user create a new service request.

When rendered, the processTrain component shows the total number of pages in the process as well as the page where the user is currently at, and allows the user to navigate between those pages. For example, Figure 11-13 shows the first page in the create service request process, where the user selects one appliance from a listbox and enters a description of the problem in a textbox. The number of nodes (circles) in the train indicates the total number of predefined pages in the process; the solid node indicates that the user is currently working on that page in the process.

Figure 11-13 First Page of the Create New Service Request Process (SRCreate.jspx)

Create service request page to enter product and description

The processChoiceBar component renders a dropdown menu, and where applicable, buttons for navigating forward and backward in the process.

On the first page, when the user clicks Confirm or Continue (or selects Confirm from the dropdown menu), the application displays the second page of the create service request process, as shown in Figure 11-14.

Figure 11-14 Second Page of the Create New Service Request Process (SRCreateConfirm.jspx)

Confirmation page for creating service request

From the second page, the user can return to the problem description page by clicking Basic Problem Details in the train or clicking the Back button, or by selecting Basic Problem Details from the dropdown menu.

If done the user clicks Submit Request, and the application displays the Request Submitted page, as shown in Figure 11-15.

Figure 11-15 Request Submitted Page (SRCreateDone.jspx)

Request Submitted page for creating service request

11.5.1 How to Create a Process Train

To display a process train on each page, you bind the processTrain component to a process train model. At runtime the train model dynamically creates the train for each page in the process.

To create and use a process train:

  1. Create a process train model. (See Section 11.5.1.1, "Creating a Process Train Model")

  2. Create the JSF page for each node in the train. (See Section 11.5.1.2, "Creating the JSF Page for Each Train Node")

  3. Create a navigation rule that has navigation cases for each node. (See Section 11.5.1.3, "Creating the JSF Navigation Rules")

11.5.1.1 Creating a Process Train Model

Use the oracle.adf.view.faces.model.MenuModel class and the oracle.adf.view.faces.model.ProcessMenuModel class to create a process train model that dynamically generates a process train. The MenuModel class is the same menu model mechanism that is used for creating menu tabs and menu bars, as described in Section 11.2.1, "How to Create Dynamic Navigation Menus".

To create a process train model:

  1. Create a class that can get and set the properties for each node in the process train.

    Each node in the train needs to have a label, a viewId and an outcome property. Example 11-37 shows part of the MenuItem class used in the SRDemo application.

    Example 11-37 MenuItem.java for Process Train Nodes

    package oracle.srdemo.view.menu;
    ...
    public class MenuItem {
        private String _label          = null;
        private String _outcome        = null;
        private String _viewId         = null;
        ...
        
        //extended security attributes
        private boolean _readOnly = false;
        private boolean _shown = true; 
     
        public void setLabel(String label) {
            this._label = label;
        }
     
        public String getLabel() {
            return _label;
        }
     
        public void setOutcome(String outcome) {
            this._outcome = outcome;
        }
     
        public String getOutcome() {
            return _outcome;
        }
     
        public void setViewId(String viewId) {
            this._viewId = viewId;
        }
     
        public String getViewId() {
            return _viewId;
        }
     
        public void setReadOnly(boolean readOnly) {
            this._readOnly = readOnly;
        }
     
        public boolean isReadOnly() {
            return _readOnly;
        }
     
        public void setShown(boolean shown) {
            this._shown = shown;
        }
     
        public boolean isShown() {
            return _shown;
        }
        ...
    }
    
    
  2. Configure a managed bean for each node in the train, with values for the properties that require setting at instantiation.

    Each bean should be an instance of the class you create in step 1. Example 11-38 shows the managed bean code for the process train nodes in faces-config.xml.

    Example 11-38 Managed Beans for Process Train Nodes in the faces-config.xml File

    <!--First train node -->
    <managed-bean>
      <managed-bean-name>createTrain_Step1</managed-bean-name>  
      <managed-bean-class>oracle.srdemo.view.menu.MenuItem</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope> 
      <managed-property>
        <property-name>label</property-name>
        <value>#{resources['srcreate.train.step1']}</value>
      </managed-property>
      <managed-property>
        <property-name>viewId</property-name>
        <value>/app/SRCreate.jspx</value>
      </managed-property>
      <managed-property>
        <property-name>outcome</property-name>
        <value>GlobalCreate</value>
      </managed-property>
    </managed-bean>
    
    <!-- Second train node-->
    <managed-bean>
      <managed-bean-name>createTrain_Step2</managed-bean-name>  
      <managed-bean-class>oracle.srdemo.view.menu.MenuItem</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope> 
      <managed-property>
        <property-name>label</property-name>
        <value>#{resources['srcreate.train.step2']}</value>
      </managed-property>
      <managed-property>
        <property-name>viewId</property-name>
        <value>/app/SRCreateConfirm.jspx</value>
      </managed-property>
      <managed-property>
        <property-name>outcome</property-name>
        <value>Continue</value>
      </managed-property>
    </managed-bean>
    
    
  3. Configure a managed bean that is an instance of a list with application as its scope.

    The list entries are the train node managed beans you create in step 2, listed in the order that they should appear on the train. Example 11-39 shows the managed bean code for creating the process train list.

    Example 11-39 Managed Bean for Process Train List in the faces-config.xml File

    <!-- create the list to pass to the train model -->
    <managed-bean>
      <managed-bean-name>createTrainNodes</managed-bean-name>
      <managed-bean-class>java.util.ArrayList</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
      <list-entries>
        <value-class>oracle.srdemo.view.menu.MenuItem</value-class>
        <value>#{createTrain_Step1}</value>
        <value>#{createTrain_Step2}</value>
      </list-entries>
    </managed-bean>
    
    
  4. Create a class to facilitate the construction of a ProcessMenuModel instance. This class must have at least two properties, viewIdProperty and instance.

    Example 11-40 shows the TrainModelAdapter class used in the SRDemo application.

    Example 11-40 TrainModelAdapter.java for Holding the Process Train Nodes

    package oracle.srdemo.view.menu;
    import java.beans.IntrospectionException;
    import java.io.Serializable;
    import oracle.adf.view.faces.model.MenuModel;
    import oracle.adf.view.faces.model.ProcessMenuModel;
     
    public class TrainModelAdapter implements Serializable {
        private String _propertyName = null;
        private Object _instance = null;
        private transient MenuModel _model = null;
        private Object _maxPathKey = null;
     
        public MenuModel getModel() throws IntrospectionException {
            if (_model == null)
            {
              _model = new ProcessMenuModel(getInstance(), 
                                            getViewIdProperty(),
                                            getMaxPathKey());
            }
            return _model;
        }
     
        public String getViewIdProperty() {
            return _propertyName;
        }
     
        /**
         * Sets the property to use to get at view id
         * @param propertyName
         */
        public void setViewIdProperty(String propertyName) {
            _propertyName = propertyName;
            _model = null;
        }
     
        public Object getInstance() {
            return _instance;
        }
     
        /**
         * Sets the treeModel
         * @param instance must be something that can be converted into a TreeModel
         */
        public void setInstance(Object instance) {
            _instance = instance;
            _model = null;
        }
     
        public Object getMaxPathKey()
        {
          return _maxPathKey;
        }
     
        public void setMaxPathKey(Object maxPathKey)
        {
          _maxPathKey = maxPathKey;
        } 
    }
    
    

    If you wish to write your own menu model instead of using ProcessMenuModel, you can use ProcessUtils to implement the PlusOne or MaxVisited behavior for controlling page access. For information about how to control page access using those process behaviors, see Section 11.5.1.1.1, "What You May Need to Know About Controlling Page Access".

  5. Configure a managed bean to reference the class you create in step 4. This is the bean to which the processTrain component is bound.

    The bean should be instantiated to have the instance property value set to the managed bean that creates the train list (as configured in step 3). The instantiated bean should also have the viewIdProperty value set to the viewId property on the bean created in step 1. Example 11-41 shows the managed bean code for creating the process train model.

    Example 11-41 Managed Bean for Process Train Model in the faces-config.xml File

    <!-- create the train menu model -->
    <managed-bean>
      <managed-bean-name>createTrainMenuModel</managed-bean-name>
      <managed-bean-class>
        oracle.srdemo.view.menu.TrainModelAdapter</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
      <managed-property>
        <property-name>viewIdProperty</property-name>
        <value>viewId</value>
      </managed-property>
      <managed-property>
        <property-name>instance</property-name>
        <value>#{createTrainNodes}</value>
      </managed-property>
    </managed-bean>
    
    

11.5.1.1.1 What You May Need to Know About Controlling Page Access

When you want to control the pages users can access based on the page they are currently on, you can use one of two process scenarios provided by ADF Faces, namely Max Visited or Plus One.

Suppose there are five pages or nodes in a process train, and the user has navigated from page 1 to page 4 sequentially. At page 4 the user jumps back to page 2. Where the user can go next depends on which process scenario is used.

In the Max Visited process, from the current page 2 the user can go back to page 1, go ahead to page 3, or jump ahead to page 4. That is, the Max Visited process allows the user to return to a previous page or advance to any page up to the furthest page already visited. The user cannot jump ahead to page 5 from page 2 because page 5 has not yet been visited.

Given the same situation, in the Plus One process the user can only go ahead to page 3 or go back to page 1. That is, the Plus One process allows the user to return to a previous page or to advance one node in the train further than they are on currently. The user cannot jump ahead to page 4 even though page 4 has already been visited.

If you were to use the Max Visited process, you would add code similar to the next code snippet, for the createTrainMenuModel managed bean (see Example 11-41) in faces-config.xml:

<managed-property>
  <property-name>maxPathKey</property-name>
  <value>TRAIN_DEMO_MAX_PATH_KEY</value>
</managed-property>

ADF Faces knows to use the Max Visited process because a maxPathKey value is passed into the ProcessMenuModel (see Example 11-40).

The Create New Service Request process uses the Plus One process because faces-config.xml doesn't have the maxPathKey managed-property setting, thus null is passed for maxPathKey. When null is passed, ADF Faces knows to use the PlusOne process.

The process scenarios also affect the immediate and readOnly attributes of the command component used within a processTrain component. For information, see Section 11.5.1.2.1, "What You May Need to Know About the Immediate and ReadOnly Attributes".

11.5.1.2 Creating the JSF Page for Each Train Node

Each train node has its own page. To display the process train, on each page bind the processTrain component to the process train model, as shown in Example 11-42.

A processTrain component is usually inserted in the location facet of a panelPage or page component. Like a menu component, a processTrain component has a nodeStamp facet that accepts one commandMenuItem component. It is the commandMenuItem component that provides the actual label you see below a train node, and the navigation outcome when the label is activated.

Example 11-42 ProcessTrain Component in the SRCreate.jspx File

<af:panelPage..>
  ...
  <f:facet name="location">
    <af:processTrain var="train"
                     value="#{createTrainMenuModel.model}">
      <f:facet name="nodeStamp">
        <af:commandMenuItem text="#{train.label}"
                            action="#{train.getOutcome}"
                            readOnly="#{createTrainMenuModel.model.readOnly}"
                            immediate="false"/>
      </f:facet>
    </af:processTrain>
  </f:facet>
  ...
</af:panelPage>


Note:

You can use the same code for the process train on each page because the process train model dynamically determines the train node links, the order of the nodes, and whether the nodes are enabled, disabled, or selected.

Typically, you use a processTrain component with a processChoiceBar component. The processChoiceBar component, which is also bound to the same process train model, gives the user additional navigation choices for stepping through the multipage process. Example 11-43 shows the code for the processChoiceBar component in the SRCreate.jspx page. A processChoiceBar component is usually inserted in the actions facet of a panelPage or page component.

Example 11-43 ProcessChoiceBar Component in the SRCreate.jspx File

<af:panelPage ..>
  <f:facet name="actions">
    <af:panelButtonBar>
      <af:commandButton text="#{res['srdemo.cancel']}"
                        action="#{backing_SRCreate.cancelButton_action}"
                        immediate="true"/>
      <af:processChoiceBar var="choice"
                           value="#{createTrainMenuModel.model}">
        <f:facet name="nodeStamp">
          <af:commandMenuItem text="#{choice.label}"
                              action="#{choice.getOutcome}"
                              readOnly="#{createTrainMenuModel.model.readOnly}"
                              immediate="false"/>
        </f:facet>
      </af:processChoiceBar>
    </af:panelButtonBar>
  </f:facet>
  ...
</af:panelPage>

As illustrated in Figure 11-13 and Figure 11-14, the processChoiceBar component automatically provides a Continue button and a Back button for navigating forward and backward in the process. You don't have to write any code for these buttons. If you want to provide additional buttons (such as the Cancel and Submit Request buttons in Figure 11-14), use a panelButtonBar to lay out the button components and the processChoiceBar component.


Note:

If your multipage process has only two pages, ADF Faces uses Continue as the label for the button that navigates forward. If there is more than two pages in the process, the forward button label is Next.

11.5.1.2.1 What You May Need to Know About the Immediate and ReadOnly Attributes

The two process scenarios provided by ADF Faces and described in Section 11.5.1.1.1, "What You May Need to Know About Controlling Page Access" have an effect on both the immediate and readOnly attributes of the commandMenuItem component used within processTrain. When binding processTrain to a process train model, you can bind the node's immediate or readOnly attribute to the model's immediate or readOnly attribute. The ProcessMenuModel class then uses logic to determine the value of the immediate or readOnly attribute.

When the data on the current page does not need to be validated, the immediate attribute should be set to true. For example, in the Plus One scenario described in Section 11.5.1.1.1, if the user is on page 4 and goes back to page 2, the user has to come back to page 4 again later, so that data does not need to be validated when going to page 1 or 3, but should be validated when going ahead to page 5.

The ProcessMenuModel class uses the following logic to determine the value of the immediate attribute:

  • Plus One: immediate is set to true for any previous step, and false otherwise.

  • Max Visited: When the current page and the maximum page visited are the same, the behavior is the same as the Plus One scenario. If the current page is before the maximum page visited, then immediate is set to false.

The readOnly attribute should be set to true only if that page of the process cannot be reached from the current page. The ProcessMenuModel class uses the following logic to determine the value of the readOnly attribute:

  • Plus One: readOnly will be true for any page past the next available page.

  • Max Visited: When the current step and the maximum page visited are the same, the behavior is the same as the Plus One scenario. If the current page is before the maximum page visited, then readOnly is set to true for any page past the maximum page visited.

11.5.1.3 Creating the JSF Navigation Rules

The <from-outcome> and <to-view-id> values in the navigation cases must match the properties set in the process train model.

In the SRDemo application, a global navigation rule is used for the first page of the Create New Service Request process because the SRCreate.jspx page is accessible from any page in the application. The second page of the process, SRCreateConfirm.jspx, is not included in the global navigation rule because it is only accessible from the SRCreate.jspx page. Example 11-44 shows the navigation rules and cases for the process.

Example 11-44 Navigation Rules for Process Train Nodes in the faces.config.xml File

<navigation-rule>
  <from-view-id>*</from-view-id>
  <navigation-case>
    <from-outcome>GlobalCreate</from-outcome>
    <to-view-id>/app/SRCreate.jspx</to-view-id>
  </navigation-case>
  ...
</navigation-rule>
...
<navigation-rule>
  <from-view-id>/app/SRCreate.jspx</from-view-id>
  <navigation-case>
    <from-outcome>Continue</from-outcome>
    <to-view-id>/app/SRCreateConfirm.jspx</to-view-id>
  </navigation-case>
  ...
</navigation-rule>

<navigation-rule>
  <from-view-id>/app/SRCreateConfirm.jspx</from-view-id>
  <navigation-case>
    <from-outcome>Back</from-outcome>
    <to-view-id>/app/SRCreate.jspx</to-view-id>
  </navigation-case>
  <navigation-case>
    <from-outcome>Complete</from-outcome>
    <to-view-id>/app/SRCreateDone.jspx</to-view-id>
  </navigation-case>
</navigation-rule>

11.5.2 What Happens at Runtime

Java automatically adds a no-arg constructor to TrainModelAdapter because the TrainModelAdapter class is used as a managed bean. TrainModelAdapter constructs the process train model, which is a ProcessMenuModel instance, via the createTrainMenuModel managed bean. The createTrainNodes managed bean creates and injects the train node list into the train model. The train model provides the model that correctly highlights and enables the nodes on the train as you step through the process.

The individual train node managed beans (for example, createTrain_Step1) are instantiated with values for label, viewId, and outcome that are used by the train model to dynamically generate the train nodes. The default JSF actionListener mechanism uses the outcome values to handle the page navigation.

In the SRDemo application, the individual train node managed beans access String resources in the resource bundle via the resources managed bean, so that the correct node label is dynamically retrieved and display at runtime.

At runtime if maxPathKey has a value (set in faces-config.xml), ADF Faces knows to use the Max Visited process scenario. If maxPathKey is null (as in the SRDemo application), ADF Faces uses the Plus One process to control page access from the current page.

Like the menuTab component, the processTrain and processChoiceBar components have a nodeStamp facet, which takes one commandMenuItem component. By using train as the variable and binding the processTrain component to the process train model, you need only one commandMenuItem component to display all train node items using #{train.label} as the text value and #{train.getOutcome} as the action value on the command component. Similarly, by using choice as the variable and binding the processChoiceBar component to the process train model, you need only one commandMenuItem component to display all items as menu options using #{choice.label} as the text value and #{choice.getOutcome} as the action value.

The enabling and disabling of a node is not controlled by the MenuItem class, but by the process train model based on the current view using the EL expression #{createTrainMenuModel.model.readOnly} on the readOnly attribute of the processTrain or processChoiceBar component.


Tip:

Disabled menu choices are not rendered on browsers that don't support disabled items in a dropdown menu. On browsers that support disabled items in a dropdown menu, the unreachable items will look disabled.

11.5.3 What You May Need to Know About Process Trains and Menus

The ProcessMenuModel class extends the ViewIdPropertyMenuModel class, which is used to create dynamic menus, as described in Section 11.2, "Using Dynamic Menus for Navigation". Like menus and menu items, each node on a train is defined as a menu item. But unlike menus where the menu items are gathered into the intermediate menu tree object (MenuTreeModelAdapter), the complete list of train nodes is gathered into an ArrayList that is then injected into the TrainModelAdapter class. Note, however, that both ViewIdPropertyMenuModel and ProcessMenuModel can always take a List and turn it into a tree internally.

In the SRDemo application, the nodes on the train are not secured by user role as any user can create a new service request, which means that the train model can be stored as an application scoped managed bean and shared by all users. The menu model is stored as a session scoped managed bean because the menu tab items are secured by user role, as some tabs are not available to some user roles.

To add a new page to a process train, configure a new managed bean for the page (Example 11-38), add the new managed bean to the train list (Example 11-39), and add the navigation case for the new page (Example 11-44).