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

6 Creating a Basic Page

This chapter describes how to use the Data Control Palette to create databound forms using ADF Faces components.

This chapter includes the following sections:

6.1 Introduction to Creating a Basic Page

You can create UI widgets that allow you to display and collect information using data controls created for your business services. For example, using the Data Control Palette, you can drag an attribute for an item, and then choose to display the value as either read-only text or as an input text field with a label.

Instead of having to drop individual attributes, JDeveloper allows you to drop all attributes for an object at once as a form. The actual UI components that make up the form depend on the type of form dropped.

Once you drop the UI components, you can then drop built-in operations as command UI components that allow users to operate on the data. For example, you can create buttons that allow users to navigate between data objects displayed in the form. You can also modify the default components to suit your needs.

This chapter explains the following:

6.2 Using Attributes to Create Text Fields

To create text fields, you bind ADF Faces text UI components to attributes on a data control using an attribute binding. JDeveloper allows you to do this declaratively without the need to write any code. Additionally, JDeveloper provides a complete WYSIWYG development environment for your JSF pages, meaning you can design most aspects of your pages without needing to look at the code.

6.2.1 How to Use the Data Control Palette to Create a Text Field

To create a text field that can display or update an attribute, you must bind the UI component to the attribute using a data control. JDeveloper allows you to do this declaratively by dragging and dropping an attribute of a collection from the Data Control Palette.

To create a bound text field:

  1. From the Data Control Palette, select an attribute for a collection (for a description of which icon represents attributes and other objects in the Data Control Palette, see Section 5.2.1, "How to Understand the Items on the Data Control Palette").

    For example, Figure 6-1 shows the problemDescription attribute under the ServiceRequest collection for the findAllServiceRequest() method of the SRPublicFacade data control in the SRDemo application. This is the attribute to drop to display the problem description for a service request.

    Figure 6-1 Attributes Associated with a Returned Object in the Data Control Palette

    Attributes for service requests below the collection

    If you wish to create input text fields used to collect data, you can use either a custom method or one of the data control's built-in creation methods. For procedures, see Section 10.7, "Creating an Input Form for a New Record".

  2. Drag the attribute onto the page, and from the context menu choose the type of widget to display or collect the attribute value. For an attribute, you are given the following choices:

    • Texts

      • ADF Output Text with a Label: Creates a panelLabelAndMessage component that holds an ADF Faces outputText component. The label attribute on the panelLabelAndMessage component is populated.

      • ADF Output Text: Creates an ADF Faces outputText component. No label is created.

      • ADF Input Text with a Label: Creates an ADF Faces inputText component with a validator. The label attribute is populated.

        Tip:

        For more information about validators, see Chapter 12, "Using Validation and Conversion".
      • ADF Input Text: Creates an ADF Faces inputText component with a validator. The label attribute is not populated.

      • ADF Label: An ADF Faces outputLabel component.

    • Single selections

      These widgets display lists. For the purposes of this chapter, only the text widgets will be discussed. To learn about lists and their bindings, see Section 11.7, "Creating Databound Dropdown Lists".

6.2.2 What Happens When You Use the Data Control Palette to Create a Text Field

When you drag an attribute onto a JSF page and drop it as a UI component, among other things, a page definition file is created for the page (if one does not already exist), using the name of the JSF page including the page's package name, and appending PageDef as the name of the page definition file. For example, the page definition file for the SREdit page is app_staff_SREditPageDef.xml. For a complete account of what happens when you drag an attribute onto a page, see Section 5.2.3, "What Happens When You Use the Data Control Palette". Bindings for the iterators and methods are created and added to the page definition file if they do not already exist, as are the bindings for each attribute. Additionally, the necessary JSPX page code for the UI component is added to the JSF page.

6.2.2.1 Creating and Using Iterator Bindings

Whenever you create UI components on a page by dropping an item that is part of a collection from the Data Control Palette (or you drop the whole collection as a form or table), JDeveloper creates a method iterator binding if it does not already exist. A method iterator binding references an iterator for the data collection, which facilitates iterating over its data objects. It also manages currency and state for the data objects in the collection. An iterator binding does not actually access the data. Instead, it simply exposes the object that can access the data, and it specifies the current data object in the collection. Other bindings then refer to the iterator binding in order to return data for the current object or to perform an action on the object's data.

Tip:

There is one iterator created for each collection. This means that when you drop two attributes from the same collection (or drop the collection twice), they use the same iterator. This is fine, unless you need the iterator to behave differently for the different components. In that case, you will need to manually create separate iterators. For procedures and an example, see Section 10.9, "Conditionally Displaying the Results Table on a Search Page".

For example, if you drop the problemDescription attribute under the ServiceRequest collection for the findAllServiceRequest() method, JDeveloper creates a method iterator binding for the returned ServiceRequest collection.

The iterator binding's rangeSize attribute determines how many records will be available for the page each time the iterator binding is accessed. This attribute gives you a relative set of 1-N rows positioned at some absolute starting location in the overall rowset. By default, it is set to 10. For more information about using this attribute, see Section 6.4.2.2, "Iterator RangeSize Attribute". Example 6-1 shows the method iterator binding created when you drop an attribute from the ServiceRequest collection for the findAllServiceRequest() method.

Example 6-1 Page Definition Code for a Method Iterator Binding When You Drop an Attribute from a Method Return Collection

<executables>
    <methodIterator id="findAllServiceRequestIter"
                    Binds="findAllServiceRequest.result"
                    DataControl="SRPublicFacade" RangeSize="10"
                    BeanClass="oracle.srdemo.model.entities.ServiceRequest"/>
</executables>

For information regarding the iterator binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".

JDeveloper also creates an action binding for the findAllServiceRequest method used to return the collection. Note that this action binding is created as a binding element and not an executable element. Example 6-2 shows the action binding created when you drop an attribute of the ServiceRequest collection for the findAllServiceRequest() method.

Example 6-2 Page Definition code for an Action Binding Used by the Iterator

<bindings>
    <methodAction id="findAllServiceRequest"
                  InstanceName=""SRPublicFacade.dataProvider"
                  DataControl="SRPublicFacade"
                  MethodName="findAllServiceRequest" RequiresUpdateModel="true"
                  Action="999"
                  ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                            dataProvider_findAllServiceRequest_result"/>

This metadata allows the ADF binding container to access the attribute by allowing the iterator to access the result property on the associated method binding. Because the iterator is an executable, it is created when the page is loaded, and thus causes the method referenced in the method binding to execute.

In Example 6-1, the iterator is bound to the result property of the findAllServiceRequest method binding. This means that the iterator will manage all the returned service requests, including determining the current service request or range of service requests.

For information regarding the action binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".

6.2.2.2 Creating and Using Value Bindings

When you drop an attribute from the Data Control Palette, JDeveloper creates an attribute binding that is used to bind the UI component to the attribute's value. This type of binding presents the value of an attribute for a single object in the current row in the collection. Value bindings can be used to both display and collect attribute values.

For example, if you drop the problemDescription attribute under the ServiceRequest collection for the findAllServiceRequest() method as an ADF Output Text w/Label widget onto the SREdit page, JDeveloper creates an attribute binding for the problemDescription attribute. Note that the attribute value references the findAllServiceRequestIter iterator. This allows the binding to access the attribute value of the current record. Example 6-3 shows the attribute binding for problemDescription created when you drop the attribute from the ServiceRequest collection for the findAllServiceRequest() method.

Example 6-3 Page Definition Code for an Attribute Binding

<bindings>
    ...
    <attributeValues id="ServiceRequestproblemDescription"
                     IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="problemDescription"/>
      </AttrNames>
    </attributeValues>
  </bindings>

For information regarding the attribute binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".

6.2.2.3 Using EL Expressions to Bind UI Components

When you create a text field by dropping an attribute from the Data Control Palette, JDeveloper creates the UI component associated with the widget dropped by writing the corresponding code to the JSF page.

For example, when you drop the ProblemDescription attribute as an Output Text w/Label widget, JDeveloper creates an EL expression that binds the panelLabelAndMessage label's attribute to the label property of the ProblemDescription binding. It creates another expression that binds the panelLabelAndMessage value attribute to the inputValue property of the ProblemDescription binding, which in turn is the value of the ProblemDescription attribute. For more information about binding object properties, see Section A.2.2, "Oracle ADF Data Binding Files".

Example 6-4 shows the code generated on the JSF page when you drop an attribute as an Output Text w/Label widget.

Example 6-4 JSF Page Code for an Attribute Dropped as an Output Text w/Label

<af:panelLabelAndMessage  
         label="#{bindings.ServiceRequestproblemDescription.label}">
    <af:outputText
         value="#{bindings.problemDescription.inputValue}"/>
</af:panelLabelAndMessage>

If instead you drop the problemDescription attribute as an Input Text w/Label widget, JDeveloper creates an inputText component. As Example 6-5 shows, similar to the output text component, the value is bound to the inputValue property of the problemDescription binding. Additionally, the following properties are also set:

  • label: bound to the binding's label property.

  • required: bound to the binding's mandatory property. See Section 12.3, "Adding Validation" for more information about this property.

  • columns: bound to the displayWidth property. This determines how wide the text box will be.

Example 6-5 JSF Page Code for an Attribute Dropped as an Input Text w/Label

<af:inputText value="#{bindings.problemDescription.inputValue}"
                       label="#{bindings.problemDescription.label}"
                       required="#{bindings.problemDescription.mandatory}"
                       columns="#{bindings.problemDescription.displayWidth}">
  <af:validator binding="#{bindings.problemDescription.validator}"/>
</af:inputText>

For more information about the properties, see Appendix B, "Reference ADF Binding Properties".

6.2.3 What Happens at Runtime: The JSF and ADF Lifecycles

When a page is submitted and a new page requested, the application invokes both the JSF lifecycle and the ADF lifecycle. The JSF lifecycle handles the components at the view layer, while the ADF lifecycle handles the data at the model layer.

Specifically, the JSF lifecycle handles the submission of values on the page, validation for components, navigation, and displaying the components on the resulting page and saving and restoring state. The JSF lifecycle phases use a UI component tree to manage the display of the faces components. This tree is a runtime representation of a JSF page: each UI component tag in a page corresponds to a UI Component instance in the tree. The FacesServlet object manages the request processing lifecycle in JSF applications. FacesServlet creates an object called FacesContext, which contains the information necessary for request processing, and invokes an object that executes the lifecycle.

The ADF lifecycle handles preparing and updating the data model, validating the data at the model layer, and executing methods on the business layer. The ADF lifecycle uses the binding container to make data available for easy referencing by the page during the current page request.

The lifecycles are divided into phases. For the two lifecycles to work together, the ADF lifecycle phases are integrated with the JSF lifecycle phases using the JSF event listener mechanism. The ADF lifecycle listens for phase events using the ADF phase listener, which allows the appropriate ADF phases to be executed before or after the appropriate JSF phases.

When an ADF Faces component bound to an ADF data control is inserted into a JSF page for the first time, JDeveloper adds the ADF PhaseListener to faces-config.xml. Example 6-6 shows the ADF PhaseListener configuration in faces-config.xml.

Example 6-6 Registering the ADF PhaseListener in faces-config.xml

<faces-config xmlns="http://java.sun.com/JSF/Configuration">
  ...
<lifecycle>
  <phase-listener>
    oracle.adf.controller.faces.lifecycle.ADFPhaseListener
  </phase-listener>
</lifecycle>
  ...
</faces-config>

Figure 6-2 shows how the JSF and ADF phases integrate in the lifecycle.

Figure 6-2 The Lifecycle of a Page Request in an ADF Application Using ADF Faces Components

The ADF and JSF phases work together

In a JSF application that uses the ADF Model layer, the lifecycle is as follows:

  • Restore View: The URL for the requested page is passed to the bindingContext, which finds the page definition that matches the URL. The component tree of the requested page is newly built or restored. All the component tags, event handlers, converters, and validators on the submitted page have access to the FacesContext instance. If it's a new empty tree (that is, there is no data from the submitted page), the lifecycle proceeds directly to the Render Response phase. Otherwise, the Restore View phase issues an event which the Initialize Context phase of the ADF Model layer lifecycle listens for and then executes.

    For example, for a page that contains an inputText UI component bound to the ProblemDescription attribute of a ServiceRequest returned collection, when the URL is passed in, the page definition is exposed. The UI component is then built. If data is to be displayed, the Initialize Context phase executes. Otherwise, the lifecycle jumps to the Render Response phase.

  • Initialize Context: The page definition file is used to create the bindingContainer object, which is the runtime representation of the page definition for the requested page. The LifecycleContext class used to persist information throughout the ADF lifecycle phases is instantiated and initialized.

  • Prepare Model: The binding container is refreshed, which sets any page parameters contained in the page definition. Any entries in the executables section of the page definition are refreshed, depending on the value of the Refresh and RefreshCondition attributes.

    The Refresh and RefreshCondition attributes are used to determine when and whether to invoke an executable. For example, there maybe an executable that should only be invoked under certain conditions. Refresh determines the phase in which to invoke the executable, while the refresh condition determines whether the condition has been met. Set the Refresh attribute to prepareModel when your bindings are dependent on the outcome from the operation.

    If Refresh is set to prepareModel, or if no value is supplied (meaning it uses the default, ifneeded), then the RefreshCondition attribute value is evaluated. If no RefreshCondition value exists, the executable is invoked. If a value for RefreshCondition exists, then that value is evaluated, and if the return value of the evaluation is true, then the executable is invoked. If the value evaluates to false, the executable is not invoked. The default value always enforces execution. If the incoming request contains no POST data or query parameters, then the lifecycle forwards to the Render Response phase.

    For more information, see . For details about the refresh attribute, see Section A.7.1, "PageDef.xml Syntax".

    In the problem description example, the bindingContainer invokes the findAllServiceRequestIter method iterator, which in turn invokes the findAllServiceRequest method that returns the ServiceRequest collection. The iterator then iterates over the data and makes the data for the first found record available to the UI component by placing it in the binding container. Because there is a binding for the problemDescription attribute in the page definition that can access the value from the iterator (see Example 6-3), and since the UI component is bound to the problemDescription binding using an EL expression (#{bindings.problemDescription.inputValue}), that data is displayed by that component.

  • Apply Request Values: Each component in the tree extracts new values from the request parameters (using its decode method) and stores it locally. Most associated events are queued for later processing. If a component has its immediate attribute set to true, then the validation, conversion, and events associated with the component are processed during this phase. For more information about validation and conversion, see Chapter 12, "Using Validation and Conversion".

    For example, if a user enters a new value into the inputText component, that value is stored locally using the setSubmittedValue method on the inputText component.

  • Process Validations: Local values of components are converted and validated. If there are errors, the lifecycle jumps to the Render Response phase. At the end of this phase, new component values are set, any validation or conversion error messages and events are queued on FacesContext, and any value change events are delivered.

    For a detailed explanation of this and the next two phases of the lifecycle, see Chapter 12, "Using Validation and Conversion".

  • Update Model Values: The component's validated local values are moved to the model and the local copies are discarded.

  • Validate Model Updates: The updated model is now validated against any validation routines set on the model.

  • Invoke Application: Any action bindings for command components or events are invoked. For a detailed explanation of this and the next two phases of the lifecycle, see Section 9.4, "Using Dynamic Navigation". For a description of action bindings used to invoke business logic, see Section 6.4, "Incorporating Range Navigation into Forms".

  • Metadata Commit: Changes to runtime metadata are committed. This phase is not used in this release, but will be used in future releases.

  • Initialize Context (only if navigation occurred in the Invoke Application lifecycle): The page definition for the next page is initialized.

  • Prepare Model (only if navigation occurred in the Invoke Application lifecycle): Any page parameters contained in the next page's definition are set. Any entries in the executables section of the page definition are used to invoke the corresponding methods in the order they appear.

  • Prepare Render: The binding container is refreshed to allow for any changes that may have occurred in the Apply Request Values or Validation phases. The prepareRender event is sent to all registered listeners.

    Note:

    Instead of displaying prepareRender as a valid phase for a selection, JDeveloper displays renderModel, which represents the refresh(RENDER_MODEL) method called on the binding container.

    You should set the Refresh attribute of an executable to renderModel when the refreshCondition is dependent on the model. For example, if you want to use the #{adfFacesContext.postback} expression in a RefreshCondition of an executable, you must set the Refresh property to either renderModel or renderModelIfNeeded, which will cause the method to be executed during the prepareRender phase. For more information, see .

  • Render Response: The components in the tree are rendered as the J2EE web container traverses the tags in the page. State information is saved for subsequent requests and the Restore View phase.

6.3 Creating a Basic Form

Instead of dropping individual attributes to create a form, JDeveloper allows you to drop a complete form that displays or collects data for all the attributes on an object. For example, the SREdit page was created by dropping the return from the findServiceRequestById method, which contains all the attributes necessary to edit a given service request.

This section provides information on creating a form that returns data to be viewed or edited. You can also use a constructor or the method itself to create a form used to populate data instead of return data. For more information about creating that type of form, see Section 10.7, "Creating an Input Form for a New Record".

6.3.1 How to Use the Data Control Palette to Create a Form

To create a form using a data control, you bind the UI components to the attributes on the corresponding object in the data control. JDeveloper allows you to do this declaratively by dragging and dropping a collection or a structured attribute from the Data Control Palette.

These procedures are for creating a form that displays all objects from a collection returned by a method that takes no parameters. If you want to use a collection returned from a method that takes parameters, you need to have those parameters set in order for the form to display the proper records. For procedures and information about setting parameters, see Section 10.6.1, "How to Create a Form or Table Using a Method That Takes Parameters".

To create a form that allows a user to create a new record, you need to use a method that creates the new instance, given some values for that instance. If your data control was configured to support updates, then it will include constructors, which are objects you can use to create a form that creates a new object, populating values for all attributes on the object. For more information, see Section 10.7, "Creating an Input Form for a New Record".

Whether you use a collection, a constructor, or a method to create a form, you may also want to add a command button that invokes a method to, for example, insert the data into the data source or update the data. For procedures and more information, see Section 10.3, "Creating Command Components to Execute Methods".

To create a basic form:

  1. From the Data Control Palette, select a collection that is a return of a findAll method.

    To display the value of existing attributes, drop the returned collection from a method used to find records. Figure 6-3 shows the ServiceRequest collection for the findAllServiceRequest() method from the SRDemo application. This method creates a form with data already populated in the input text fields.

    Figure 6-3 Attributes Associated with a Returned Collection in the Data Control Palette

    ServiceRequest collection under findAllServiceRequest method
  2. Drag the collection onto the page, and from the context menu choose the type of form to display or collect data for the object. For a form, you are given the following choices:

    • ADF Form: Launches the Edit Form Fields dialog that allows you to select individual attributes instead of creating a field for every attribute by default. It also allows you to select the label and UI component used for each attribute. By default, ADF inputText components are used, except for dates, which use the selectInputDate component. Each inputText component contains a validator tag that allows you to set up validation for the attribute. For more information, see Section 12.3, "Adding Validation".

      You can elect to include navigational controls that allow users to navigate through all the data objects in the collection. For more information, see Section 6.4, "Incorporating Range Navigation into Forms". You can also elect to include a Submit button used to submit the form. This button submits the HTML form and applies the data in the form to the bindings as part of the JSF/ADF page lifecycle. For additional help in using the dialog, click Help. All UI components are placed inside a panelForm component.

    • ADF Read-Only Form: Same as the ADF Form, but by default, outputText components are used. Since the form is meant to display data, no validator tags are added. The label attribute is populated for each component. Attributes of type Date also use the outputText component. All components are placed inside panelLabelAndMessage components, which are in turn placed inside a panelForm component.

    • ADF Creation Form: Not to be used when using TopLink Java objects and an EJB session bean. Use constructors or custom methods instead. For more information, see Section 10.7, "Creating an Input Form for a New Record".

  3. If you are building a form that allows users to update data, you now need to drag and drop a method that will perform the update. For more information, see Section 10.3, "Creating Command Components to Execute Methods".

6.3.2 What Happens When You Use the Data Control Palette to Create a Form

Dropping an object as a form from the Data Control Palette has the same effect as dropping a single attribute, except that multiple attribute bindings and associated UI components are created. The attributes on the UI components (such as value) are bound to properties on that attribute's binding object (such as inputValue). Example 6-7 shows the code generated on the JSF page when you drop the ServiceRequest collection for the findAllServiceRequest() method as a default ADF Form.

Example 6-7 Code on a JSF Page for an Input Form

<af:panelForm>
      <af:inputText value="#{bindings.svrId.inputValue}"
                    label="#{bindings.svrId.label}"
                    required="#{bindings.svrId.mandatory}"
                    columns="#{bindings.svrId.displayWidth}">
        <af:validator binding="#{bindings.svrId.validator}"/>
        <f:convertNumber groupingUsed="false"
                         pattern="#{bindings.svrId.format}"/>
      </af:inputText>
      <af:inputText value="#{bindings.status.inputValue}"
                    label="#{bindings.status.label}"
                    required="#{bindings.status.mandatory}"
                    columns="#{bindings.status.displayWidth}">
        <af:validator binding="#{bindings.status.validator}"/>
      </af:inputText>
      <af:selectInputDate value="#{bindings.requestDate.inputValue}"
                          label="#{bindings.requestDate.label}"
                          required="#{bindings.requestDate.mandatory}">
        <af:validator binding="#{bindings.requestDate.validator}"/>
        <f:convertDateTime pattern="#{bindings.requestDate.format}"/>
      </af:selectInputDate>
      <af:inputText value="#{bindings.problemDescription.inputValue}"
                    label="#{bindings.problemDescription.label}"
                    required="#{bindings.problemDescription.mandatory}"
                    columns="#{bindings.problemDescription.displayWidth}">
        <af:validator binding="#{bindings.problemDescription.validator}"/>
      </af:inputText>
      <af:selectInputDate value="#{bindings.assignedDate.inputValue}"
                          label="#{bindings.assignedDate.label}"
                          required="#{bindings.assignedDate.mandatory}">
        <af:validator binding="#{bindings.assignedDate.validator}"/>
        <f:convertDateTime pattern="#{bindings.assignedDate.format}"/>
      </af:selectInputDate>
      <f:facet name="footer">
        <af:commandButton text="Submit"/>
      </f:facet>
</af:panelForm>

Note:

For information regarding the validator and converter tag, see Chapter 12, "Using Validation and Conversion".

6.3.2.1 Using Facets

JSF components use facet tags to hold other components that require a special relationship with the parent component, for example, headers and footers for columns within a table, or the footer facet for the panelForm component. When you use the Data Control Palette to drop a widget, any preferred facets are included.

Many components use facets, and when you use widgets to create complex components (such as panelForm), output tags are often automatically created and inserted into the facets. You can manually edit these components or add other components to facets.

When you choose to include a Submit button when you drop a collection as an input form, a command button is added to the panelForm's footer facet. This command button causes the form that holds the panelForm to be submitted. By default, the text is Submit. Figure 6-4 shows the command button in the panelForm's footer facet.

Figure 6-4 Footer Facet for the Panel Form

Footer facet in the structure window and the form

Example 6-8 shows the corresponding code in the JSF page.

Example 6-8 Facet in a JSF Page

<af:panelForm>
... 
  <f:facet name="footer">
    <af:commandButton text="Submit"/>
  </f:facet>
</af:panelForm>

Each facet can hold only one component. If you need a facet to hold more than one component, then you need to nest those components in a container component, which can then be nested in the facet. For an example of how the panelGroup and panelButtonBar components are used to group all buttons in the footer facet of a form, see Section 6.4.2.3, "Using EL Expressions to Bind to Navigation Operations".

Also note that JDeveloper displays all facets available to the component in the Structure window. However, only those that contain UI components appear activated. Any empty facets are "grayed" out. Figure 6-5 shows both full and empty facets for a panelPage component

Figure 6-5 Empty and Full Facet Folders in the Structure Window

All the facets for the PanelPage in the Structure window.

6.4 Incorporating Range Navigation into Forms

When you choose to add navigation when you use the Data Control Palette to create an input form, JDeveloper includes ADF Faces command components bound to existing navigational logic on the data control. This built-in logic allows the user to navigate through the data objects in the collection. Figure 6-6 shows a form that contains navigation buttons.

Figure 6-6 Navigation in a Form

Navigation buttons in the detail page for search results

6.4.1 How to Insert Navigation Controls into a Form

By default, when you choose to include navigation when creating a form using the Data Control Palette, JDeveloper creates First, Last, Previous, and Next buttons that allow the user to navigate within the collection.

You can also add navigation buttons to an existing form manually.

To manually add navigation buttons:

  1. From the Data Control Palette, select the operation associated with the collection of objects on which you wish the operation to execute, and drag it onto the JSF page.

    For example, if you want to navigate through service requests, you would drag the Next operation associated with the ServiceRequest collection of the findAllServiceRequest() method. Figure 6-7 shows the navigation operations associated with a collection.

    Figure 6-7 Navigation Operations Associated With a Collection

    Navigation operations in the Data Control Palette
  2. Choose either Command Button or Command Link from the context menu.

6.4.2 What Happens When Command Buttons Are Created Using the Data Control Palette

When you drop any operation as a command component, JDeveloper:

  • Defines an action binding in the page definition file for the associated operations

  • Inserts code in the JSF page for the command components

6.4.2.1 Using Action Bindings for Built-in Navigation Operations

Action bindings execute business logic. Action bindings can invoke methods on a business service (for example, the method action binding for a method used by an iterator to access a collection) or as in the case of navigation controls, they can invoke built-in methods on the action binding object. These built-in methods operate on the iterator or on the data control itself, and are represented as operations in the Data Control Palette. JDeveloper provides navigation operations that allow users to navigate forward, backwards, to the last object in the collection, and to the first object.

Note:

For more information about using methods to add, remove, or update data, see Chapter 10, "Creating Command Components to Execute Methods".

Like value bindings, action bindings for navigation operations must also contain a reference to the iterator binding, as it is used to determine the current object and can therefore determine the correct object to display when each of the navigation buttons is clicked. Example 6-9 shows the action bindings for the navigation operations.

Tip:

The numerical values of the Action attribute in the <action> tags (as shown in Figure 6-7) are defined in the oracle.adf.model.meta.OperationDefinition class. However, when you use the ADF Model layer's action binding editor, you never need to set the numerical code by hand.

Example 6-9 Page Definition Code for an Operation Action Binding

<action id="First" RequiresUpdateModel="true" Action="12"
            IterBinding="findAllServiceRequestIterator"/>
 <action id="Previous" RequiresUpdateModel="true" Action="11"
            IterBinding="findAllServiceRequestIterator"/>
 <action id="Next" RequiresUpdateModel="true" Action="10"
            IterBinding="findAllServiceRequestIterator"/>
 <action id="Last" RequiresUpdateModel="true" Action="13"
            IterBinding="findAllServiceRequestIterator"/>

6.4.2.2 Iterator RangeSize Attribute

Iterator bindings have a rangeSize attribute used to determine the number of data objects to return for each iteration. This attribute helps in situations when the number of objects in the data source is quite large. Instead of returning all objects, only a set number are returned and accessible by the other bindings. Once the iterator reaches the end of the range, it accesses another set, creating a new range. Example 6-10 shows the range size for the findAllServiceRequestIter iterator.

Note:

This rangeSize attribute is not the same as the row attribute on a table component. For more information, see Table 7-1, "ADF Faces Table Attributes and Populated Values".

Example 6-10 RangeSize Attribute for an Iterator

<executables>
  <methodIterator id="findAllServiceRequestIter"
                      Binds="findAllServiceRequest.result"
                      DataControl="SRPublicFacade" RangeSize="10"
                      BeanClass="oracle.srdemo.model.entities.ServiceRequest"/>
</executables>

By default, the rangeSize attribute is set to 10. This means that a user can view 10 objects, navigating back and forth between them, without needing to access the data source. The iterator keeps track of the current object. Once a user clicks a button that requires a new range (for example, clicking the Next button on object number 10), the binding object executes its associated method against the iterator, and the iterator retrieves another set of 10 records. The bindings then work with that set. You can change this setting as needed. You can set it to -1 to have the full record set returned. The default is -1 for iterator bindings that furnish a list of valid choices for list bindings.

Tip:

You can also set a range of records directly in the query you write on your business service. However, doing so means every page that uses the query will return the same range size. By setting the range size on the iterator, you can control the size per page.

Table 6-1 shows the built-in navigation operations provided on data controls, along with the action attribute value set in the page definition, and the result of invoking the operation or executing an event bound to the operation. For more information about action events, see Section 6.4.3, "What Happens at Runtime: About Action Events and Action Listeners".

Table 6-1 Built-in Navigation Operations

Operation Action Attribute Value When invoked, the associated iterator binding will...

Next

10

Move its current pointer to the next object in the result set. If this object is outside the current range, the range is scrolled forward a number of objects equal to the range size.

Previous

11

Move its current pointer to the preceding object in the result set. If this object is outside the current range, the range is scrolled backward a number of objects equal to the range size.

First

12

Move its current pointer to the beginning of the result set.

Last

13

Move its current pointer to the end of the result set.

Next Set

14

Move the range forward a number of objects equal to the range size attribute.

Previous Set

15

Move the range backward a number of objects equal to the range size attribute.

SetCurrentRowWithKey

96

Set the row key as a String converted from the value specified by the input field. The row key is used to set the currency of the data object in the bound data collection. For an example of when this is used, see Section 7.7.1, "How to Manually Set the Current Row".

SetCurrentRowWithKeyValue

98

Set the current object on the iterator, given a key's value.


Every action binding for an operation has an enabled boolean property that the ADF framework sets to false when the operation should not be invoked. You can then bind the UI component to this value to determine whether or not the component should be enabled. For more information about the enabled property, see Appendix B, "Reference ADF Binding Properties".

6.4.2.3 Using EL Expressions to Bind to Navigation Operations

When you create command components using navigation operations, the components are placed in a panelButtonBar component. JDeveloper creates an EL expression that binds a navigational command button's actionListener attribute to the execute property of the action binding for the given operation. This expression causes the binding's operation to be invoked on the iterator when a user clicks the button.

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". For example, the First command button's actionListener attribute is bound to the execute method on the First action binding.

The disabled attribute is used to determine if the button should be inactivated. For example, if the user is currently displaying the first record, the First button should not be able to be clicked. The code uses an EL expression that evaluates to the enabled property on the action binding. If the property value is not true (for example, if the current record is the first record, the First operation should not be enabled), then the button is disabled. Example 6-11 shows the code generated on the JSF page for navigation operation buttons.

Example 6-11 JSF Code for Navigation Buttons Bound to ADF Operations

<f:facet name="footer">
   <af:panelButtonBar>
     <af:commandButton actionListener="#{bindings.First.execute}"
                         text="First"
                         disabled="#{!bindings.First.enabled}"/>
     <af:commandButton actionListener="#{bindings.Previous.execute}"
                       text="Previous"
                       disabled="#{!bindings.Previous.enabled}"/>
     <af:commandButton actionListener="#{bindings.Next.execute}"
                       text="Next"
                       disabled="#{!bindings.Next.enabled}"/>
     <af:commandButton actionListener="#{bindings.Last.execute}"
                       text="Last"
                       disabled="#{!bindings.Last.enabled}"/>
   </af:panelButtonBar>
   <af:commandButton text="Submit"/>
 </f:facet>

6.4.3 What Happens at Runtime: About Action Events and Action Listeners

An action event occurs when a command component is activated. For example, when a user clicks a button, the form the component is enclosed in is submitted, and subsequently an action event is fired. Action events might affect only the user interface (for example, a link to change the locale, causing different field prompts to display), or they might involve some logic processing in the back end (for example, a button to navigate to the next record).

An action listener is a class that wants to be notified when a command component fires an action event. An action listener contains an action listener method that processes the action event object passed to it by the command component.

In the case of the navigation operations, when a user clicks, for example, the Next button, an action event is fired. This event stores currency information about the current data object, taken from the iterator. Because the component's actionListener attribute is bound to the execute method of the Next action binding, the Next operation is invoked. This method takes the currency information passed in the event object to determine what the next data object should be.

6.4.4 What You May Need to Know About the Browser Back Button

When a user clicks the navigation buttons, the iterator determines the next data object to display. However, when the user clicks the browser's Back button, the action and/or event is not shared outside the browser, and the iterator is bypassed. Therefore, when a user clicks a browser's Back button instead of using navigation buttons on the page, the iterator becomes out of sync with the page displayed, causing unexpected results.

For example, say a user browses to object 103, and then uses the browser's Back button. Because the browser shows the page last visited, object 102 is shown. However, the iterator still thinks the current object is 103 because the iterator was bypassed. If the user were to then to click the Next button, object 104 would display because that is what the iterator believes to be the next object, and not 103 as the user would expect.

Because the iterator and the page are out of sync, problems can arise when a user edits records. For example, if the user were to have edited object 102 after clicking the browser's Back button, the changes would have actually been posted to 103, because this is what the iterator thought was the current object.

To prevent a user making changes to the wrong object instances, you can use token validation. When you enable token validation for a page, that page is annotated with the current object for all the iterators used to render that page. This annotation is encoded onto the HTML payload rendered to the browser and is submitted to the server along with any data. At that point, the current object of the iterator is compared with the annotation. If they are different, an exception is thrown.

For example, in the earlier scenario, when the user starts at 103 but then clicks the browser's Back button to go to 102, as before, the previous page is displayed. However, that page was (and still is) annotated with 102. Therefore, when the user clicks the Next button to submit the page and navigate forward, the annotation (102) does not match the iterator (which is still at 103), an exception is thrown, and the Next operation is not executed. The page renders with 103, which is the object the iterator believed to be current. An error displays on the page stating that 102 was expected, since the server expected 102 based on the annotation submitted with the data. Since 103 is now displayed, both the annotation and the iterator are now at 103, and are back in sync.

Token validation is set on the page definition for a JSF page. By default, token validation is on.

To set token validation:

  1. Open the page definition file for the page.

  2. In the Structure window, select the root node for the page definition itself.

  3. In the Property Inspector, use the dropdown list for the EnableTokenValidation attribute to set validation to true to turn on token validation, or false to turn off token validation.

Example 6-12 shows a page definition file after token validation was set to true.

Example 6-12 Enable Token Validation in the Page Definition File

<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                version="10.1.3.35.29" id="createProductPageDef"
                Package="oracle.srdemo.view.pageDefs"
                EnableTokenValidation="true">

6.5 Modifying the UI Components and Bindings on a Form

Once you use the Data Control Palette to create a form, you can then delete attributes, change the order in which they are displayed, change the component used to display them, and change the attribute to which they are bound.

6.5.1 How to Modify the UI Components and Bindings

You can modify certain aspects of the default components dropped from the Data Control Palette. You can use the Structure window to change the order in which components are displayed, to add new components or change existing components, or to delete components. You can use the Property Inspector to change or delete bindings, or to change the label displayed for a component.

To modify default components and bindings:

  1. Use the Structure window to do the following:

    • Change the order of the UI components by dragging them up or down the tree. A black line with an arrowhead denotes where the UI component will be placed.

    • Add a UI component for a new attribute. Right-click an existing UI component in the Structure window and choose to place the new component before, after, or inside the selected component. You then choose from a list of UI components.

      To bind the new component to an attribute, you need to use the Property Inspector. See the first bullet point in step 2 for details.

    • Delete a UI component. Right-click the component and choose Delete. If you wish to keep the component, but delete just the binding, you need to use the Property Inspector. See the second bullet point in step 2.

  2. With the UI component selected in the Structure window, you can then do the following in the Property Inspector:

    • Add a binding for the UI component. Enter an EL expression in the Value field, or click the ellipsis (...) button in the Value field to open the EL Expression Builder. To select a binding available from the data control, select the ADF Bindings > Bindings node. This node shows the operations, iterators, and attributes available from the collection currently bound, as well as the binding properties. For more information about using EL expressions, see Section 5.6, "Creating ADF Data Binding EL Expressions".

    • Delete a binding for the UI component by deleting the EL expression.

    • Change the binding. You can rebind the component to any other attribute, or any property on another attribute. For procedures, see Section 6.5.1.1, "Changing the Value Binding for a UI Component".

    • Change the label for the UI component. By default, the label is bound to the binding's label property (for more information about this property, see Appendix B, "Reference ADF Binding Properties".

      You can also change the label just for the current page. To do so, select the Label attribute. You can enter text or an EL expression to bind the label value to something else, for example, a key in a properties or resource file.

      For example, the inputText component used to enter the status of a service request would have the following for its Label attribute:

      #{bindings.status.label}
      

      In this expression, status is the ID for the attribute binding in the page definition file.

      However, you could change the expression to instead bind to a key in a properties file, for example:

      #{srproperties['sr.status']}
      

      In this example, srproperties is a variable defined in the JSF page used to load a properties file. The SREdit page uses a variable named res. The label for the cancel button has the following value:

      #{res['srdemo.cancel']}
      

      For more information about using resource bundles, see Section 14.4, "Internationalizing Your Application".

6.5.1.1 Changing the Value Binding for a UI Component

Instead of modifying a binding, you can completely change the object to which the UI component in a form is bound.

To rebind a UI component:

  1. From the Data Control palette, drag the collection or attribute that you now want the component to be bound to, and drop it on the component.

    OR

    Right-click the UI component in the Structure window and choose Edit Binding. Either the Attribute, Table, or List Binding Editor launches, depending on the UI component for which you are changing the binding.

  2. In the context menu, select Bind existing <component name>.

6.5.1.2 Changing the Action Binding for a UI Component

When a component is bound to a built-in operation, you can change the action using the Action Binding Editor.

To rebind a UI Command component:

  1. Right-click the command component in the Structure window and choose Edit Binding, which launches the Action Binding Editor.

  2. In the editor, use the dropdown menu to select a different action.

6.5.2 What Happens When You Modify Attributes and Bindings

When you modify how an attribute is displayed by moving the UI component or changing the UI component, JDeveloper changes the corresponding code on the JSF page. When you use the binding editors to add or change a binding, JDeveloper adds the code to the JSF page, and also adds the appropriate elements to the page definition file.