Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3.0)

Part Number B28967-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

10 Creating More Complex Pages

This chapter describes how to add more complex bindings to your pages, such as using methods that take parameters to create forms and command components.

This chapter includes the following sections:

10.1 Introduction to More Complex Pages

Once you create a basic page and add navigation capabilities, you may want to add more complex features to your pages, such as the ability to pass parameter values from one page to another, or the ability to search for and return certain records. ADF provides many features that allow you to add this complex functionality using very little actual code.

For example, you can create command components, such as buttons, that directly execute methods on your service bean. You only need to drag the method from the data control and drop it as a command button.You can also use command components to pass parameter values to another page.

In addition to creating basic forms that display information from a data store, you can also create forms that display only certain records (determined by parameter values), forms that allow you to create new records, and forms that search through and return objects based on a user's input.

Read this chapter to understand:

10.2 Using a Managed Bean to Store Information

Often, pages require information from other pages in order to display correct information. Instead of setting this information directly on a page (for example, by setting the parameter value on the page's page definition file), which essentially hardcodes the information, you can store this information on a managed bean. As long as the bean is stored in a scope that is accessible, any value for an attribute on the bean can be accessed using an EL expression.

For example, the SREdit page requires the value of the svrId attribute for the row selected by the user on the previous page. This value provides the parameter value for the findServiceRequestById(Integer) method used to display the form. Additionally, the method bound to the Cancel button needs to return an outcome allowing the user to return to the correct page (the SREdit page can be accessed from three different pages). The SRDemo application has a managed bean that holds this information, allowing the sending page to set the information, and the SREdit page to use the information in order to determine where to navigate for the Cancel action. This information is stored as a hash map in the bean.

Managed beans are Java classes that you register with the application using the faces-config.xml file. When the JSF application starts up, it parses this configuration file and the beans are made available and can be referenced in an EL expression, allowing access to the beans' properties and methods. Whenever a managed bean is referenced for the first time and it does not already exist, the Managed Bean Creation Facility instantiates the bean by calling the default constructor method on the bean. If any properties are also declared, they are populated with the declared default values.

10.2.1 How to Use a Managed Bean to Store Information

Using the JSF Configuration Editor in JDeveloper, you can create a managed bean and register it with the JSF application at the same time.

To create a managed bean:

  1. Open the faces-config.xml file. This file is stored in the <project_name>/WEB-INF directory.

  2. At the bottom of the window, select the Overview tab.

  3. In the element list on the left, select Managed Beans. Figure 10-1 shows the JSF Configuration Editor for the faces-config.xml file.

    Figure 10-1 The JSF Configuration Editor

    The JSF Configuration Editor shows all the managed beans
  4. Click the New button to open the Create Managed Bean dialog, as shown in Figure 10-2. Enter the name and fully qualified class path for the bean. Select a scope, select the Generate Java File checkbox, and click OK.

    Figure 10-2 The Create Managed Bean Dialog

    Fields to enter name, class, and scope

    Tip:

    If the managed bean will be used by multiple pages in the application, you should set the scope to Session. However, then the bean cannot contain any reference to the binding container, as the data on the binding object is on the request scope, and therefore cannot "live" beyond a request. For examples of when you may need to reference the binding container, see Section 10.5, "Overriding Declarative Methods".
  5. You can optionally use the arrow to the left of the Managed Properties bar to display the properties for the bean. Click New to create any properties. Press F1 for additional help with the configuration editor.

    Note:

    While you can declare managed properties using the configuration editor, the corresponding code is not generated on the Java class. You will need to add that code by creating private member fields of the appropriate type and then using the Generate Accessors... menu item on the context menu of the code editor to generate the corresponding getter and setter methods for these bean properties.

10.2.2 What Happens When You Create a Managed Bean

When you use the configuration editor to create a managed bean, and elect to generate the Java file, JDeveloper creates a stub class with the given name and a default constructor. Example 10-1 shows the code added to the MyBean class stored in the view package.

Example 10-1 Generated Code for a Managed Bean

package view;
 
public class MyBean {
    public MyBean() {
    }
}

JDeveloper also adds a managed-bean element to the faces-config.xml file. This declaration allows you to easily access any logic on the bean using an EL expression that refers to the given name. Example 10-2 shows the managed-bean element created for the MyBean class.

Example 10-2 Managed Bean Configuration on the faces-config.xml File

<managed-bean>
  <managed-bean-name>my_bean</managed-bean-name>
  <managed-bean-class>view.MyBean</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

You now need to add the logic required by your pages and methods in your application. You can then refer to that logic using an EL expression that refers to the managed-bean-name given to the managed bean. For example, to access the myInfo property on the bean, the EL expression would be:

#{my_bean.myInfo}

The following sections of this chapter provide examples of using the SRDemo application's userState managed bean (view.UserSystemState.java) to hold or get information. Please see those sections for more detailed examples of using a managed bean to hold information.

10.3 Creating Command Components to Execute Methods

When you create a UI component by dragging and dropping a collection that is a return of a method, that method is executed when the page is rendered, so that it can return the collection. However, you can also drag the method itself (or any other type of method) and drop it as a command component to directly invoke the method.

Tip:

You can also drop the method as a parameter form. For more information, see Section 10.7.3, "How to Use a Custom Method to Create an Input Form" and Section 10.8, "Creating Search Pages".

In addition to custom methods, your data control may contain built-in methods that perform some standard business logic, such as updating or deleting objects. You can use these methods in conjunction with a collection. For example, the SRDemo application contains the mergeEntity(Object) method on the SRPublicFacade bean that can be used to update any object and merge it in the data source.

When you drag this method from the Data Control Palette and drop it as command button, the button is automatically bound to the method, and so that method will execute whenever the button is clicked. Figure 10-3 shows some of the methods in the Data Control Palette for the SRDemo application.

Figure 10-3 Methods in the Data Control Palette

Methods in the Data Control Palette

Whether using a custom method or a built-in method, you can create a command button that executes the associated business logic on your service bean by binding the button to that method. When you use the Data Control Palette to create the button, JDeveloper creates the binding for you. You need only to set the values for any parameters needed by the method.

10.3.1 How to Create a Command Component Bound to a Service Method

In order to perform the required business logic, many methods require a value for the method's parameter or parameters. That means when you create a button bound to the method, you need to specify from where the value for the parameter(s) can be retrieved.

For example, if you use the mergeEntity(Object) method, you need to specify the object to be updated.

To add a button bound to a method:

  1. From the Data Control Palette, drag the method onto the page.

    Tip:

    If you are dropping a button for a method that needs to work with data in a table or form, that button must be dropped inside the table or form.
  2. From the context menu, choose Methods > ADF Command Button.

  3. If the method takes parameters, the Action Binding dialog opens. In the Action Binding Editor, click the ellipses (...) button in the Value column of Parameters to launch the EL Expression Builder. You use the builder to set the value of the method's parameter.

    For example, to set the value for the Object parameter of the mergeEntity(Object) method used to update a collection, you would:

    1. In the EL Expression Builder, expand the ADF Bindings node and then expand the bindings node.

      All the bindings in the JSF page's page definition file are displayed.

    2. Expand the node for the iterator that works with the object you want to merge.

    3. Expand the currentRow node. This node represents the current row in the iterator.

    4. Select the dataProvider property and click the right-arrow button. Doing so creates an EL expression that evaluates to the data for the object in the current row of the iterator. Click OK to close the EL Expression Builder and populate the value with the EL expression. Click OK again to bind the button to the method.

    Tip:

    Consider criteria such as the following when determining what to select for the parameter value:
    • If you want the method to operate on a row in a table, you would set the parameter to be the current row in the table binding, and not the current object in the iterator.

    • If you want to be able to update multiple rows that represent detail objects in a master-detail relationship, you can set the parameter to be the master list object.

    • If the value is stored in a scope or on a managed bean, you would select the corresponding attribute in that scope or on that managed bean. However, this value must be available when this method is executed. For more information, see Section 10.4, "Setting Parameter Values Using a Command Component".

10.3.2 What Happens When You Create Command Components Using a Method

When you drop a method as a command button, JDeveloper:

  • Defines a method action binding for the method. If the method takes any parameters, JDeveloper creates NamedData elements that hold the parameter values.

  • Inserts code in the JSF page for the ADF Faces command component. This code is the same as code for any other command button, as described in Section 6.4.2.3, "Using EL Expressions to Bind to Navigation Operations". However, instead of being bound to the execute method of the action binding for a built-in operation, the button is bound to the execute method of the method action binding for the method that was dropped.

10.3.2.1 Using Parameters in a Method

As when you drop a collection that is a return of a method, when you drop a method that takes parameters onto a JSF page, JDeveloper creates a method action binding (for details, see Section 6.2.2.1, "Creating and Using Iterator Bindings"). However, when the method requires parameters to run, JDeveloper also creates NamedData elements for each parameter. These elements represent the parameters of the method.

For example, the mergeEntity(Object) method action binding contains a NamedData element for the Object parameter. This element is bound to the value specified when you created the action binding. Example 10-3 shows the method action binding created when you dropped the mergeEntity(Object) method, and bound the Object parameter (named entity) to the data for the current row in the findServiceRequestByIdIter iterator.

Example 10-3 Method Action Binding for a Parameter Method

<methodAction id="mergeEntity" InstanceName="SRPublicFacade.dataProvider"
                  DataControl="SRPublicFacade" MethodName="mergeEntity"
                  RequiresUpdateModel="true" Action="999"
                  ReturnName="SRPublicFacade.methodResults.SRPublicFacade
                        _dataProvider_mergeEntity_result">
  <NamedData NDName="entity"
             NDValue="${bindings.findServiceRequestByIdIter.
                        currentRow.dataProvider}"
             NDType="java.lang.Object"/>
</methodAction>

10.3.2.2 Using EL Expressions to Bind to Methods

Like creating command buttons using navigation operations, when you create a command button using a method, JDeveloper binds the button to the method using the actionListener attribute. The button is bound to the execute property of the action binding for the given method. This binding causes the binding's method to be invoked on the business service. For more information about the command button's actionListener attribute, see Section 6.4.3, "What Happens at Runtime: About Action Events and Action Listeners".

Tip:

Instead of binding a button to the execute method on the action binding, you can bind the button to method in a backing bean that overrides the execute method. Doing so allows you to add logic before or after the original method runs. For more information, see Section 10.5, "Overriding Declarative Methods".

Like navigation operations, the disabled property on the button uses an EL expression to determine whether or not to display the button. Example 10-4 shows the EL expression used to bind the command button to the mergeEntity(Object) method.

Example 10-4 JSF Code to Bind a Command Button to a Method

<af:commandButton actionListener="#{bindings.mergeEntity.execute}"
                  text="mergeEntity"
                  disabled="#{!bindings.mergeEntity.enabled}"/>

Tip:

When you drop a UI component onto the page, JDeveloper automatically gives it an ID based on the number of that component previously dropped, for example, commandButton1, commandButton2. You may want to change the ID to something more descriptive, especially if you will need to refer to it in a backing bean that contains methods for multiple UI components on the page. Note that if you do change the ID, you must manually update any references to it in EL expressions in the page.

10.3.3 What Happens at Runtime

When the user clicks the button, the method binding causes the associated method to be invoked, passing in the value bound to the NamedData element as the parameter. For example, if a user clicks a button bound to the mergeEntity(Object) method, the method takes the value of the current record and updates the data source accordingly.

10.4 Setting Parameter Values Using a Command Component

There may be cases where an action on one page needs to set the parameters, for example, for a method used to display data on another page. As Figure 10-4 shows, the SRList page in the SRDemo application uses command links, which the user can click in order to directly edit a service request.

Figure 10-4 Command Links Used in a Table

Command links in a table set the row and navigate to SREdit

The commandLink component is used to both navigate to the SREdit page and to set the needed parameter for the findServiceRequestById(Integer) method used to create the form that displays the data on the SREdit page. You can use the ADF Faces setActionListener component to set parameters.

Tip:

The SRDemo application does not use the setActionListener component for this page. Instead, this same functionality is provided by methods on a managed bean, as more than one page needs the same logic to set this parameter for the SREdit page. When logic needed for one page is also needed by other pages, it might be beneficial to place that logic on a managed bean. For more information about using managed beans, see Section 10.2, "Using a Managed Bean to Store Information".

10.4.1 How to Set Parameters Using Command Components

You can use the setActionListener component to set values on other objects. This component must be a child to a command component.

To use the setActionListener component:

  1. Create a command component using either the Data Control Palette or the Component Palette.

  2. From the Component Palette, drag a setActionListener component and drop it as a child to the command component.

  3. In the Insert ActionListener dialog, set From to be the parameter value.

  4. Set To to be where you want to set the parameter value.

    Tip:

    Consider storing the parameter value on a managed bean or in scope instead of setting it directly on the resulting page's page definition file. By setting it directly on the next page, you lose the ability to easily change navigation in the future. For more information, see Section 10.2, "Using a Managed Bean to Store Information". Additionally, the data in a binding container is valid only during the request in which the container was prepared. Therefore, the data may change between the time you set it and the time the next page is rendered
  5. If the parameter value is required by a method used to create a parameterized form on another page, when you create the form, set the parameter to the value of the To attribute in step 4. For more information, see Section 10.6, "Creating a Form or Table Using a Method that Takes Parameters".

10.4.2 What Happens When You Set Parameters

The setActionListener component lets the command component set a value before navigating. When you set the From attribute to the source of the value you need to pass, the component will be able to access that value. When you set the To attribute to a target, the command component is able to set the value on the target. Example 10-5 shows the code on the JSF page for a command link that accesses the data for the current row as the from value and sets that as the value of an attribute on a managed bean using the To attribute.

Example 10-5 JSF Page Code for a Command Link Using a setActionListener Component

<af:commandLink actionListener="#{bindings.setCurrentRowWithKey.execute}"
                action="edit"
                text="#{row.svrId}"
                disabled="#{!bindings.setCurrentRowWithKey.enabled}"
                id="commandLink1">
  <af:setActionListener from="#{row.svrId}"
                           to="#{userState.currentSvrId}"/>
</af:commandLink>

10.4.3 What Happens at Runtime

When a user clicks the command component, before navigation occurs, the setActionListener component sets the parameter value. In Example 10-5, the setActionListener gets the current row's svrId attribute value and sets it as the value for the currentSvrId attribute on the userState managed bean. Now, any method that needs this page's current row's svrId can access it using the EL expression #{userState.currentSvrId}.

For example, when dropping the findServiceRequestById(Integer) method to create the form for the SREdit page, you would enter #{userState.currentSvrId} as the value for the Integer parameter. For more information, see Section 10.6, "Creating a Form or Table Using a Method that Takes Parameters".

10.5 Overriding Declarative Methods

When you drop a method as a command button, JDeveloper binds the button to the execute method on the associated binding object. This binding allows you to create the JSF page declaratively, without needing to write the associated binding code. However, there may be occasions when you need to add logic before or after the method executes. For example, in order to delete multiple selected rows in a table, you must add code before the delete method executes that accesses each row and makes it current. For more information, see Section 7.6.4, "How to Use the TableSelectMany Component in the Selection Facet".

JDeveloper allows you to add logic to a declarative method by creating a new method and property on a managed bean that provide access to the associated action binding. By default, this generated code executes the method of the corresponding binding. You can then add logic before or after this code. JDeveloper automatically binds the command component to this new method instead of the execute property on the binding. Now when the user clicks the button, the new method is executed.

Following are some of the instances in the SRDemo application where backing beans contain methods that inject the binding container and then add logic before or after the declarative method is executed:

10.5.1 How to Override a Declarative Method

In order to override a declarative method, you must have a managed bean to hold the new method to which the command component will be bound. If your page has a backing bean associated with it, JDeveloper adds the code needed to access the binding object to this backing bean. If your page does not have a backing bean, JDeveloper asks you to create one.

Note:

You cannot use the following procedure if the command component currently has an EL expression as its value for the Action attribute, as JDeveloper will not overwrite an EL expression. You must remove this value before continuing.

To override a declarative method:

  1. Drag the method to be overridden onto the JSF page and drop it as a UI command component.

    Doing so creates the component and binds it to the associated binding object in the ADF Model layer using the ActionListener attribute on the component.

    For more information about creating command components using methods on the Data Control Palette, see Section 10.3, "Creating Command Components to Execute Methods".

  2. On the JSF page, double-click on the component.

    In the Bind Action Property dialog, identify the backing bean and the method to which you want to bind the component using one of the following techniques:

    • If auto-binding has been enabled on the page, the backing bean is already selected for you, as shown in Figure 10-5.

      Figure 10-5 Bind Action Property Dialog for a Page with Auto-Binding Enabled

      Bind Actin Property Dialog for a Page Using Auto-binding
      • To create a new method, enter a name for the method in the Method field, which initially displays a default name.

        OR

      • To use an existing method, select a method from the dropdown list in the Method field.

      • Select Generate ADF Binding Code.

    • If the page is not using auto-binding, you can select from an existing backing bean or create a new one, as shown in Figure 10-6. For more information about auto-binding, see Section 4.5.4, "How to Use the Automatic Component Binding Feature".

      Figure 10-6 Bind Action Property Dialog for a Page with Auto-Binding Disabled

      Bind Action Property dialog for page not using auto-binding
      • Click New to create a new backing bean. The Create Managed Bean dialog is displayed. Use this dialog to name the bean and the class, and set the bean's scope.

        OR

      • Select an existing backing bean and method from the dropdown lists.

    Note:

    If you are creating a new managed bean, then you must set the scope of the bean to request. Setting the scope to request is required because the data in the binding container object that will be referenced by the generated code is on the request scope, and therefore cannot "live" beyond a request.

    Additionally, JDeveloper understands that the button is bound to the execute property of a binding whenever there is a value for the ActionListener attribute on the command component. Therefore, if you have removed that binding, you will not be given the choice to generate the ADF binding code. You need to either inject the code manually, or to set a dummy value for the ActionListener before double-clicking on the command component.

  3. After identifying the backing bean and method, click OK in the Bind Action Property dialog

    JDeveloper opens the managed bean in the source editor. Example 10-6 shows the code inserted into the bean. In this example, a command button is bound to the mergeEntity method.

    Example 10-6 Generated Code in a Backing Bean to Access the Binding Object

    public BindingContainer getBindings() {
    
          return this.bindings;
      }
        
      public void setBindings(BindingContainer bindings) {
          this.bindings = bindings;
      } 
    
      public String commandButton_action1() {
          BindingContainer bindings = getBindings();
          OperationBinding operationBinding =
              bindings.getOperationBinding("mergeEntity");
          Object result = operationBinding.execute();
          if (!operationBinding.getErrors().isEmpty()) {
              return null;
          }
          return null;
      }
    
  4. You can now add logic either before or after the binding object is accessed.

    Tip:

    To get the result of an EL expression, you need to use the ValueBinding class, as shown in Example 10-7

    Example 10-7 Accessing the Result of an EL Expression in a Managed Bean

    FacesContext fc = FacesContext.getCurrentInstance();
      ValueBinding expr = 
        fc.getApplication().
          createValueBinding("#{bindings.SomeAttrBinding.inputValue}");
    DCIteratorBinding ib = (DCIteratorBinding)
      expr.getValue(fc);
    

    In addition to any processing logic, you may also want to write conditional logic to return one of multiple outcomes depending on certain criteria. For example, you might want to return null if there is an error in the processing, or another outcome value if the processing was successful. A return value of null causes the navigation handler to forgo evaluating navigation cases and to immediately redisplay the current page.

    Tip:

    To trigger a specific navigation case, the outcome value returned by the method must exactly match the outcome value in the navigation rule in a case-sensitive way.

    The command button is now bound to this new method using the Action attribute instead of the ActionListener attribute. If a value had previously existed for the Action attribute (such as an outcome string), that value is added as the return for the new method. If there was no value, the return is kept as null.

10.5.2 What Happens When You Override a Declarative Method

When you ovrerride a declarative method, JDeveloper adds a managed property to your backing bean with the managed property value of #{bindings} (the reference to the binding container), and it adds a strongly-typed bean property to your class of the BindingContainer type which the JSF runtime will then set with the value of the managed property expression #{bindings}. JDeveloper also adds logic to the UI command action method. This logic includes the strongly-typed getBindings() method used to access the current binding container.

Example 10-8 shows the code that JDeveloper adds to the chosen managed bean. Notice that the return String "back" was automatically added to the method.

Example 10-8 Generated Code in a Backing Bean to Access the Binding Object

private BindingContainer bindings;
...
public String commandButton1_action() {
    BindingContainer bindings = getBindings();
    OperationBinding operationBinding = 
            bindings.getOperationBinding("mergeEntity");
    Object result = operationBinding.execute();
    if (!operationBinding.getErrors().isEmpty()) {
        return null;
    }
 
    return "back";
}

JDeveloper automatically rebinds the UI command component to the new method using the Action attribute, instead of the ActionListener attribute. For example, Example 10-9 shows the code on a JSF page for a command button created by dropping the mergeEntity method. Notice that the actionListener attribute is bound to the mergeEntity method, and the action attribute has a String outcome of back. If the user were to click the button, the method would simply execute, and navigate to the page defined as the toViewId for this navigation case.

Example 10-9 JSF Page Code for a Command Button Bound to a Declarative Method

<af:commandButton actionListener="#{bindings.mergeEntity.execute}"
                  text="persistEntity"
                  disabled="#{!bindings.persistEntity.enabled}"
                  id="commandButton1" 
                  action="back"/>

Example 10-10 shows the code after overriding the method on the page's backing bean. Note that the action attribute is now bound to the backing bean's method.

Example 10-10 JSF Page Code for a Command Button Bound to an Overridden Method

<af:commandButton text="persistEntity"
                  disabled="#{!bindings.mergeEntity.enabled}"
                  binding="#{backing_create.commandButton1}"
                  id="commandButton1"
                  action="#{backing_create.commandButton1_action}"/>

This code does the following:

  • Accesses the binding container

  • Finds the binding for the associated method, and executes it

  • Adds a return for the method that can be used for navigation. By default the return is null, or if an outcome string had previously existed for the button's Action attribute, that attribute is used as the return value. You can change this code as needed. For more information about using return outcomes, see Section 9.4, "Using Dynamic Navigation".

Tip:

If when you click the button that uses the overridden method, you receive this error:

SEVERE: Managed bean main_bean could not be created The scope of the referenced object: '#{bindings}' is shorter than the referring object

it is because the managed bean that contains the overriding method has a scope that is greater than request, (that is, either session or application). Because the data in the binding container referenced in the method has a scope of request, the scope of this managed bean must be set to request scope or a lesser scope.

10.6 Creating a Form or Table Using a Method that Takes Parameters

There may be cases where a form or table needs information before it can display content. For these types of pages, you create the form or table using a returned collection from a method that takes parameters. The parameter values will have been set by another page or method.

For example, the form on the SREdit page is created using the returned collection from the findServiceRequestsById(Integer) method. Instead of returning all service requests, it returns only the service request the user selected on the previous page. The command link on the previous page sets the parameter (Integer), which provides the service request's ID. For more information about using a command component to set a parameter value, see Section 10.4, "Setting Parameter Values Using a Command Component".

10.6.1 How to Create a Form or Table Using a Method That Takes Parameters

To create forms or tables that require parameters, you must be able to access the values for the parameters in order to determine the record(s) to return. For example, before creating the your form or table, you may need to add logic to a command button on another page that will set the parameter value on some object that the method can then access. For more information, see Section 10.4, "Setting Parameter Values Using a Command Component". Once that is done, you can set the parameter value for the form or table.

To create a form or table that uses parameters:

  1. From the Data Control Palette, drag a collection that is a return of a method that takes a parameter or parameters and drop it as any type of form or table.

  2. In the Edit Form Fields dialog or Edit Table Columns dialog, configure the form or table as needed and click OK.

    For help in using the dialogs, click Help.

    Because the method takes parameters, the Action Binding Editor opens, asking you to set the value of the parameters.

  3. In the Action Binding Editor, enter the value for each parameter by clicking the ellipses (...) button in the Value field to open the EL Expression Builder. Select the node that represents the value for the parameter.

    For example, Example 10-5, "JSF Page Code for a Command Link Using a setActionListener Component" shows a setActionListenerComponent setting the svrId parameter value on the userState bean as the currentSvrId attribute. To access that value, you would use #{userState.currentSvrId} as the value for the parameter.

    This editor uses the value to create the NamedData element that will represent the parameter when the method is executed. Since you are dropping a collection that is a return of the method (unlike a method bound to a command button), this method will be run when the associated iterator is executed as the page is loaded. You want the parameter value to be set before the page is rendered. This means the NamedData element needs to get this value from wherever the sending page has set it.

10.6.2 What Happens When You Create a Form Using a Method that Takes Parameters

When you use a return of a method that takes parameters to create a form, JDeveloper:

  • Creates an action binding for the method, a method iterator binding for the result of the method, and attribute bindings for each of the attributes of the object, or in the case of a table a table binding. It also creates NamedData elements for each parameter needed by the method.

  • Inserts code in the JSF page for the form using ADF Faces components.

Example 10-11 shows the action method binding created when you dropped the findServiceRequestsById(Integer) method, where the value for the findSvrId was set to the currentSvrId attribute of the UserState managed bean.

Example 10-11 Method Action Binding for a Method Return

<bindings>
  <methodAction id="findServiceHistoryById"
                InstanceName="SRPublicFacade.dataProvider"
                DataControl="SRPublicFacade"
                MethodName="findServiceHistoryById" RequiresUpdateModel="true"
                Action="999"
                ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                            dataProvider_findServiceHistoryById_result">
    <NamedData NDName="svrIdParam" NDValue="${userState.currentSvrId}"
               NDType="java.lang.Integer"/>
  </methodAction>
...
</bindings>

Note that the NamedData element will evaluate to the current service request ID on the userState bean, as set by any requesting page.

10.6.3 What Happens at Runtime

Unlike a method executed when a user clicks a command button, a method used to create a form is executed as the page is loaded. When the method is executed in order to return the data for the page, the method evaluates the EL expression for the NamedData element and uses that value as its parameter. It is then able to return the correct data. If the method takes more than one parameter, each is evaluated in turn to set the parameters for the method.

For example, when the SREdit page loads, it takes the value of the currentSvrId field on the userState managed bean, and sets it as the value of the parameter needed by the findServiceRequestsById(Integer) method. Once that method executes, it returns only the record that matches the value of the parameter. Because you dropped the return of the method to create the form, that return is the service request that is displayed.

10.7 Creating an Input Form for a New Record

You can create a form that allows a user to enter information for a new record and then commit that record into the data source. When the session bean for your data control is created, by default, constructor methods are created for objects on the data control. For example, the SRPublicFacade session bean in the SRDemo application has constructor methods for products, expertise areas, service histories, users, and service requests. The constructors provide an easy way to declaratively create an object that can be passed to a method. For example, by default, session beans have a persistEntity(Object) CRUD method that can be used to persist new records to the database (for more information about the default session bean methods, see Section 3.2.1.2, "Generating Session Facade Methods"). When you use the constructor method to create an input form, that method is called to create the object and initialize the object's variables to the entered values. You can then easily pass the constructor's results to the persistEntity method, which will create the new record in the data source.

There may be instances, however, when you need more control over how the new object is created. For example, you may want certain attributes to be populated programmatically. In this case, you might create a custom method to handle the creation of objects.

To use a custom method to create an input form, instead of dropping the collection returned from a method, you drop the method itself as a parameter form. JDeveloper automatically adds a command button bound to the method, so that the custom create method will execute when the user clicks the button.

For example, the SRDemo application's SRPublicFacade data control contains the createServiceRequest(String, Integer, Integer) method. String represents the value for the problem description, the first Integer represents the product ID, and the second Integer represents the ID of the user who created the request. This method creates a new instance of a ServiceRequest object using the values of the parameters to populate the attributes on the object; however, the product ID and the ID of the user are set by the method instead of by the user.

10.7.1 How to Use Constructors to Create an Input Form

Constructors for a data control are located in its Constructor folder. These access the default methods on the data control used to create an object (for more information, see Section 3.2.1.2, "Generating Session Facade Methods"). Figure 10-7 shows the constructors for the SRPublicFacade data control.

Figure 10-7 Constructors in the Data Control Palette

Product, ExpertiseArea, and ServiceHistory constructors

To create an input form using a constructor:

  1. From the Data Control Palette, drag the appropriate constructor onto the JSF page. Constructors can only be dropped as a form, so no context menu displays.

  2. In the Edit Form Fields dialog, set the labels, bindings, and UI components. Do not select Include Submit Button.

  3. From the Data Control Palette, drag the persistEntity(Object) method. This method persists the object created by the constructor to the database.

  4. In the context menu, choose Methods > ADF Command Button.

  5. In the Action Binding Editor, enter the value for the entity parameter by clicking the ellipses (...) button in the Value field to launch the EL Expression Builder. Since you want the entity parameter to be the result of the constructor, select the result under the action binding for the constructor, as shown in Figure 10-8.

    Figure 10-8 Result from the Product Constructor

    Result attribute under the Product action

Tip:

If you will be navigating to this page from another page that should display the newly created object when you return to it, you must set the cacheResults attribute on the iterator for the first page to false.

For example, say you have a page that lists all products, and a user can navigate from that page to another page to create a product. A button on this page both creates the product and navigates back to the list page. In order for the user to see the product just created, you must set the iterator binding for the product list to cacheResults=false. Doing so forces the iterator to reexecute when returning to the page and display the newly created product.

10.7.2 What Happens When You Use a Constructor

When you use a constructor to create an input form, JDeveloper:

  • Creates an action binding for the constructor method, a method iterator binding for the result of constructor method, and attribute bindings for each of the attributes of the object. It also creates an invoke action in the executables that causes the constructor method to execute during the Render Model phase.

  • Inserts code in the JSF page for the form using ADF Faces inputText components.

For example, to create a simple input form for products in the SRDemo application, you might drop the product constructor method from the Data Control Palette as a parameter form, and then drop the persistEntity method as a button below the form, as shown in Figure 10-9.

Note:

This page is an example only and does not exist in the SRDemo application.

Figure 10-9 A Simple Create Product Form

Create product form to enter ID, name, image and description

Example 10-12 shows the page definition file for this input form. When the invoke action binding executes, the constructor method is called using the action binding. The result of that constructor method is then bound to the iterator. When you drop the persistEntity(Object) method as a command button, that method can access the result of the constructer through the iterator.

Example 10-12 Page Definition Code for a Constructor

<executables>
   <invokeAction Binds="Product" id="invokeProduct" Refresh="renderModel"
                 RefreshCondition="${!adfFacesContext.postback and empty
                                               bindings.exceptionsList}"/>
   <methodIterator DataControl="SRPublicFacade"
                   BeanClass="oracle.srdemo.model.entities.Product"
                   Binds="Product.result" id="ProductIter"
                   Refresh="renderModel"
                   RefreshCondition="${!adfFacesContext.postback and empty
                                               bindings.exceptionsList}"/>
</executables>
<bindings>
  <methodAction DataControl="SRPublicFacade" id="Product" MethodName="Product"
                RequiresUpdateModel="true" Action="999"
                ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                                                         Product_result"
                ClassName="oracle.srdemo.model.entities.Product"/>
  <attributeValues id="description" IterBinding="ProductIter">
    <AttrNames>
      <Item Value="description"/>
    </AttrNames>
  </attributeValues>
  <attributeValues id="image" IterBinding="ProductIter">
    <AttrNames>
      <Item Value="image"/>
    </AttrNames>
  </attributeValues>
  <attributeValues id="name" IterBinding="ProductIter">
    <AttrNames>
      <Item Value="name"/>
    </AttrNames>
  </attributeValues>
  <attributeValues id="prodId" IterBinding="ProductIter">
    <AttrNames>
      <Item Value="prodId"/>
    </AttrNames>
  </attributeValues>
  <methodAction id="persistEntity" InstanceName="SRPublicFacade.dataProvider"
                DataControl="SRPublicFacade" MethodName="persistEntity"
                RequiresUpdateModel="true" Action="999"
                ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                                       dataProvider_persistEntity_result">
    <NamedData NDName="entity" NDValue="${bindings.Product.result}"
               NDType="java.lang.Object"/>
  </methodAction>
</bindings>

Anytime the constructor method is invoked, an object is created. However, since data is cached, (which allows the method to do a postback to the server), the constructor method will create the same object again when the user revisits the page, perhaps to create another object. Additionally, if errors occur, when the page is rerendered with the error message, the object would again be created.

To prevent duplicates, the invoke action's refreshCondition property is set so that the constructor will only be invoked whenever there has been no postback to the server and as long as there are no error messages. See Example 10-12 for the EL expression.

The iterator has the same refresh condition. This setting prevents the iterator from displaying the cached data used in the postback, and instead allows the form to display without any data when the user revisits the page.

10.7.3 How to Use a Custom Method to Create an Input Form

When you use a custom method to create an input form, you drag the method that can take the data populated by the user and drop it as a parameter form. In this case, since you need to create an object, you cannot drop a return. You drop the method itself.

To create an input form using a custom method:

  1. From the Data Control Palette, drag the appropriate method onto the JSF page.

  2. From the context menu select Parameters > ADF Parameter Form.

    The Edit Form Fields dialog opens, which allows you to customize the labels, bindings, and UI components before creating the form. JDeveloper automatically adds a command button bound to the method.

10.7.4 What Happens When You Use Methods to Create a Parameter Form

When you drop a method as a parameter form, JDeveloper:

  • Defines variables to hold the data values, a method binding for the method, and the attribute bindings for the associated attributes in the page definition file.

  • Inserts code in the JSF page for the form using ADF Faces inputText components and an ADF Faces command button component. This code is the same as code for any other input form or command button.

10.7.4.1 Using Variables and Parameters

Just as when you drop a collection that is a return of a method that takes parameters, when you drop the method itself onto a JSF page, JDeveloper creates NamedData elements for each parameter. However, since the user will provide the parameter values (instead of another page providing the values, as described in Section 10.6, "Creating a Form or Table Using a Method that Takes Parameters"), each NamedData element is bound to the attribute binding for the corresponding attribute.

This binding allows the method to access the correct attribute's value for the parameter on execution.

For example, the createServiceRequest method action binding contains a NamedData element for each of the parameters it takes. The NamedData elements are then bound to a corresponding attribute binding using an EL expression. Example 10-13 shows the method action binding and some of the attribute bindings created when you drop the createServiceRequest method.

Example 10-13 Method Action Binding in the Page Definition File

<bindings>
  <methodAction id="createServiceRequest" MethodName="createServiceRequest"
                RequiresUpdateModel="true" Action="999"
                DataControl="SRPublicFacade"
                InstanceName="SRPublicFacade.dataProvider"
                ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                         dataProvider_createServiceRequest_result">
    <NamedData NDName="problemDescription" NDType="java.lang.String"
               NDValue="${bindings.createServiceRequest_problemDescription}"/>
    <NamedData NDName="productId" NDType="java.lang.Integer"
               NDValue="${bindings.createServiceRequest_productId}"/>
    <NamedData NDName="createdBy" NDType="java.lang.Integer"
               NDValue="${bindings.createServiceRequest_createdBy}"/>
  </methodAction>
  <attributeValues id="problemDescription" IterBinding="variables">
    <AttrNames>
      <Item Value="createServiceRequest_problemDescription"/>
    </AttrNames>
    </attributeValues>
    <attributeValues id="productId" IterBinding="variables">
      <AttrNames>
        <Item Value="createServiceRequest_productId"/>
    </AttrNames>
      ...
  </attributeValues>
</bindings>

Note that like the attributes for a collection, attributes for a method also reference an iterator. However, instead of referencing a method iterator that accesses and iterates over the collection that the associated method returns, attributes for a creation-type method access and iterate over variables. Because this type of method has not returned an object, there is nothing to hold the values entered on the page. Variables act as the data holders.

JDeveloper creates a variable for each parameter the method takes. The variables are declared as children to the variable iterator, and are local, meaning they "live" only as long as the associated binding context. Example 10-14 shows the variable iterator and variables created when you use the createServiceRequest(String, Integer, Integer) method.

Example 10-14 Variable Iterator and Variables in the Page Definition File

<executables>
  <variableIterator id="variables">
    <variable Type="java.lang.String"
              Name="createServiceRequest_problemDescription"
              IsQueriable="false"/>
    <variable Type="java.lang.Integer" Name="createServiceRequest_productId"
              IsQueriable="false"/>
    <variable Type="java.lang.Integer" Name="createServiceRequest_createdBy"
              IsQueriable="false"/>
  </variableIterator>
</executables>

10.7.5 What Happens at Runtime

When the user enters data and submits the form, the variables are populated and the attribute binding can then provide the value for the method's parameters using the EL expression for the value of the NamedData element.

The service request creation process in the SRDemo application uses a process train, which causes the actual creation of the request to be spread out over three steps (for more information, see Section 11.5, "Creating a Multipage Process"). For the purposes of this explanation, assume the process is contained on one page. When the user enters a description in the corresponding inputText component and clicks the Create Product button, the following happens:

  • The problemDescriptionVar variable is populated with the value the user entered.

  • Because the problemDescription attribute binding refers to the variable iterator, and to the problemDescriptionVar variable specifically as its Item value, the attribute binding can get the description:

    <attributeValues id="problemDescription" IterBinding="variables">
      <AttrNames>
        <Item Value="createServiceRequest_problemDescription"/>
      </AttrNames>
    </attributeValues>
    
  • Because the Name NamedData element has an EL expression that evaluates to the item value of the problemDescription attribute binding, it can also access the value:

    <NamedData NDName="problemDescription" NDType="java.lang.String"           NDValue="${bindings.createServiceRequest_problemDescription}"/>
    
  • The createServiceRequest method is executed with each parameter taking its value from the corresponding NamedData element.

10.8 Creating Search Pages

You can create a search form using a method that finds records by taking parameters. The results can then be displayed in a table. In this type of search form, users must enter information for each parameter. Figure 10-10 shows the SRSearch search form used to find all service requests, given an ID, status, and a problem description.

Figure 10-10 The SRSearch Form

SRSearch searches for ID, status, and problem

10.8.1 How to Create a Search Form

You create search form by dropping an existing method that contains the logic to find and return the records based on parameters. This method must already exist on the data control. Figure 10-11 shows the findServiceRequestSearch(Integer, String, String) method used to create the search form shown in Figure 10-10. This method finds and returns all service requests given an ID, status, and description.

Figure 10-11 A Search Method That Takes Parameters in the Data Control Palette

Custom search method uses parameters to return data

To create the search form, you drop the method as a parameter form. You then drop the returned collection as a table to display the results. The SRSearch page hides the results table if it is the first time in a session that the user visits the page. For procedures for conditionally hiding the results table, see Section 10.9, "Conditionally Displaying the Results Table on a Search Page".

To create a search form and results table:

  1. From the Data Control Palette, drag a find method that takes parameters.

  2. From the context menu, choose Parameters > ADF Parameter Form.

  3. From the Data Control Palette, drag the return of that method and drop it as any type of table.

10.8.2 What Happens When You Use Parameter Methods

When you drop a method as a parameter form, JDeveloper:

  • Defines the following in the page definition file: variables to hold the data values, a method binding for the method, and the attribute bindings for the associated attributes.

  • Inserts code in the JSF page for the form using ADF Faces inputText components and an ADF Faces commandButton component. This code is the same as code for any other input form or command button.

Just as when you drop a collection that is a return of a method, when you drop a method that takes parameters onto a JSF page, JDeveloper creates a method action binding. However, because the method requires parameters to run, JDeveloper also creates NamedData elements for each parameter. These represent the parameters of the method. Each is bound to a value binding for the corresponding attribute. These bindings allow the method to access the correct attribute's value for the parameter on execution.

For example, the findServiceRequestSearch method action binding contains a NamedData element for each of the parameters it takes. The statusParam NamedData element is bound to the findServiceRequestSearch_statusParam attribute binding using an EL expression. Example 10-13 shows the method action binding and some of the attribute bindings created when you drop the findServiceRequestSearch method as a parameter form.

Example 10-15 Method Action Binding in the Page Definition File

<bindings>
  <methodAction id="findServiceRequestSearch"
                    MethodName="findServiceRequestSearch"
                    RequiresUpdateModel="true" Action="999"
                    DataControl="SRPublicFacade"
                    InstanceName="SRPublicFacade.dataProvider"
                    ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                             dataProvider_findServiceRequestSearch_result">
    <NamedData NDName="svrIdParam" NDType="java.lang.Integer"
               NDValue="${bindings.findServiceRequestSearch_svrIdParam}"/>
    <NamedData NDName="statusParam" NDType="java.lang.String"
               NDValue="${bindings.findServiceRequestSearch_statusParam}"/>
    <NamedData NDName="problemParam" NDType="java.lang.String"
               NDValue="${bindings.findServiceRequestSearch_problemParam}"/>
  </methodAction>
...
  <attributeValues id="svrIdParam" IterBinding="variables">
    <AttrNames>
      <Item Value="findServiceRequestSearch_svrIdParam"/>
    </AttrNames>
  </attributeValues>
  <attributeValues id="problemParam" IterBinding="variables">
    <AttrNames>
      <Item Value="findServiceRequestSearch_problemParam"/>
    </AttrNames>
  </attributeValues>
...
</bindings>

Because you dropped the method and not the return, the attributes reference a variable iterator that accesses and iterates over variables instead of a method iterator that accesses and iterates over a collection. This is because the method (unlike the returned collection) does not need to access an instance of an object; therefore, there is nothing to hold the values entered on the page. Variables act as these data holders.

JDeveloper creates a variable for each method parameter. The variables are declared as children to the variable iterator, and are local, meaning they "live" only as long as the associated binding context. Example 10-16 shows the variable iterator and variables created when using the findServiceRequestSearch method. The variable iterator is used both by the form and by the button.

Example 10-16 Variable Iterator and Variables in the Page Definition File

<executables>
  <variableIterator id="variables">
    <variable Type="java.lang.Integer"
              Name="findServiceRequestSearch_svrIdParam" IsQueriable="false"/>
    <variable Type="java.lang.String"
              Name="findServiceRequestSearch_statusParam"
              IsQueriable="false"/>
    <variable Type="java.lang.String"
              Name="findServiceRequestSearch_problemParam"
              IsQueriable="false"/>
  </variableIterator>
...
</executables>

When you then drop the returned collection for the results table, JDeveloper adds a method iterator that iterates over the returned collection. Since the results are in a table, a table binding is also created. Example 10-17 shows the code generated for the method iterator and table binding.

Example 10-17 Page Definition Code for a Returned Collection

<executables>
  <variableIterator id="variables">
...
  </variableIterator>
  <methodIterator id="findServiceRequestSearchIter"
                  Binds="findServiceRequestSearch.result"
                  DataControl="SRPublicFacade" RangeSize="10"
                  BeanClass="oracle.srdemo.model.entities.ServiceRequest"/>
</executables>
<bindings>
...
  <table id="findAllServiceRequest1" IterBinding="resultsIterator">
    <AttrNames>
      <Item Value="assignedDate"/>
      <Item Value="problemDescription"/>
      <Item Value="requestDate"/>
      <Item Value="status"/>
      <Item Value="svrId"/>
    </AttrNames>
  </table>
...
</bindings>

Note that because the same method is used, when you drop the table, a new method binding is not created. For more information, see Section 7.2.2, "What Happens When You Use the Data Control Palette to Create a Table".

10.8.3 What Happens at Runtime

When the user enters data and submits the form, the variables are populated and the attribute binding can then provide the value for the method's parameters using the EL expression for the value of the NamedDataElement.

Tip:

When the search form and results table are on the same page, the first time a user accesses the page, the table displays all records from the iterator. You can make it so that the results table does not display until the user actually executes the search. For procedures, see Section 10.9, "Conditionally Displaying the Results Table on a Search Page".

When the user enters Closed as the status in the corresponding inputText component, and clicks the command button, the following happens:

  • The findServiceRequestSearch_status variable is populated with the value Closed.

  • Because the attribute binding refers to the variable iterator, the attribute binding can get the value for status:

    <attributeValues id="status" IterBinding="variables">
        <AttrNames>
          <Item Value="findServiceRequestSearch_statusParam"/>
        </AttrNames>
      </attributeValues>
    
  • Because the NamedData element has an EL expression that evaluates to the item value of the attribute binding, the parameter can also access the value:

    <NamedData NDName="status" NDType="java.lang.String"
        NDValue="${bindings.findServiceRequests_statusParam}"/>
    
  • The findServiceRequestSearch method is executed with the parameters taking their values from the NamedData elements.

  • The findServiceRequestSearch method returns a collection of records that match the parameter values.

  • The findServiceRequestSearchIter iterator iterates over the collection, allowing the table to display the results. For more information about tables at runtime, see Section 7.2.2, "What Happens When You Use the Data Control Palette to Create a Table".

10.9 Conditionally Displaying the Results Table on a Search Page

When the search form and results table are on the same page, the first time a user accesses the page, the table displays all records from the iterator. You can make it so that the results table does not display until the user actually executes the search. Figure 10-12 shows the SRSearch page as it displays the first time a user accesses it.

Figure 10-12 Hidden Results Table for a Search Page

The results table does not display in the SRSearch page

Once the user executes a search, the results table displays, as shown in Figure 10-13.

Figure 10-13 Results Table Displayed for a Search Page

The results table displays once a search is executed.

10.9.1 How to Add Conditional Display Capabilities

To conditionally display the results table, you must enter an EL expression on the UI component (either the table itself or another component that holds the table component), that evaluates to whether this is the first time the user has accessed the search page. A field on a managed bean holds the value used in the expression.

To conditionally display the results table:

  1. Create a search form and results table on the same page. For procedures, see Section 10.8, "Creating Search Pages".

  2. Create a flag on a managed bean that will be set when the user accesses the page for the first time. For example, the userState managed bean in the SRDemo application contains the SEARCH_FIRSTTIME_FLAG parameter. An EL expression on the page needs to know the value of this parameter to determine whether or not to render the page (see step 4). When the bean is instantiated for the EL expression, the isSearchFirstTime method then checks that field. If it is null, it sets the value to True. For information about creating managed beans, see Section 10.2, "Using a Managed Bean to Store Information"

  3. On the JSF page, insert a setActionListener component into the command component used to execute this search. Set the from attribute to #{false}. Set the to attribute to the field on the managed bean created in step two. This will set that field to false whenever the button is clicked. For more information about using the setActionListener component, see Section 10.4, "Setting Parameter Values Using a Command Component".

    Example 10-18 shows the code for the Search button on the SRSearch page.

    Example 10-18 Using a setActionListener Component to Set a Value

    <af:commandButton actionListener="#{bindings.findServiceRequestSearch.execute}"
                     text="#{res['srsearch.searchLabel']}">
      <af:setActionListener from="#{false}"
                               to="#{userState.searchFirstTime}"/>
    </af:commandButton>
    
  4. On the JSF page, use an EL expression as the value of the Rendered attribute so that the UI component (the table or the UI component holding the table) only renders when the variable is a certain value.

    Example 10-19 shows the EL expression used for the value for the Rendered attribute of the panelGroup component on the SRSearch page.

    Example 10-19 JSF Code to Conditionally Display the Search Results Table

    <af:panelGroup rendered="#{!userState.searchFirstTime}">
    

    This EL expression causes the panelGroup component to render only if the searchFirstTime flag has a value of False.

10.9.2 What Happens When you Conditionally Display the Results Table

When you use a managed bean to hold a value, other objects can both set the value and access the value. For example, similar to passing parameter values, you can use the setActionListener component to set values on a managed bean that can then be accessed by an EL expression on the rendered attribute of a component.

For example, when a user accesses the SRSearch page for the first time, the following happens:

  • Because the panelGroup component that holds the table contains an EL expression for it's rendered attribute, and the EL expression references the userState bean, that bean is instantiated.

  • Because the user has not accessed page, the SEARCH_FIRSTTIME_FLAG field on the userState bean has not yet been set, and therefore has a value of null

  • Because the value is null, the isSearchFirstTime method on that bean sets the value to true.

  • The EL expression is evaluated, and because he SEARCH_FIRSTTIME_FLAG field is true, the SRSearch page displays without rendering the panel group, including the nested table.

  • When the user enters search criteria and clicks the Search button, the associated setActionListener component sets the searchFirstTime value on the userState bean to false.

  • Because there is no outcome defined for the command button, the user stays on the same page.

  • Because the searchFirstTime value is now set to false, when the page rerenders with the results, the panelGroup component displays the table with the result.