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

Previous
Previous
Next
Next
 

8.3 Using Tables and Forms to Display Master-Detail Relationships

JDeveloper enables you to create a master-detail browse page in a single declarative action using the Data Control Palette—you do not need to write any extra code, even the navigation is included. The Data Control Palette provides default master-detail widgets that you can use to display both the master and detail objects on the same page as read-only tables and forms. All you have to do is drop the accessor return for the detail object on the page and choose the type of widget you want for the master and detail objects. By default, when the master or detail object is a collection, the tables and forms include range navigation that enables the user to scroll through the objects in the collections.

To create a master-detail browse page, you bind the master and detail objects individually to an ADF Faces table or form. An iterator binding on the table or form is responsible for displaying the correct data, while the iterator binding on the detail object is responsible for displaying the correct detail data when a specific master object is displayed or selected.

For example, in the SRDemo application the ServiceRequest collection has a master-detail relationship to the serviceHistoryCollection. The SRMain page takes advantage of this by displaying a service request in a form at the top of the page and the related service history data in a table at the bottom of the page, as shown in Figure 8-2. For the purposes of the SRDemo application, the range navigation buttons included in the default master-detail components were removed. Instead, the user selects a specific service request on the SRList page and clicks View to display the SRMain master-detail page.


Tip:

To display the complete service request information on the SRMain page after the user selects a service request from the SRList page, the SRDemo application uses the table selection facet and parameter passing. For more information about these features, see Section 7.6, "Enable Row Selection in a Table".

Figure 8-2 Master-Detail Page

Master-detail page.

8.3.1 How to Display Master-Detail Related Data Using Tables and Forms

To create a master-detail table or form using a data control, you must bind the components to the master and detail objects. The Data Control Palette enables you to create both the master and detail components on one page with a single declarative action using the default master-detail widgets.

To create a master-detail page using the default ADF master-detail widgets:

  1. On the Data Control Palette, locate the accessor return that represents the appropriate detail object, as was previously shown in Figure 8-1.

  2. From the Data Control Palette, drag the accessor return and drop it on the JSF page.

  3. In the context menu, choose one of the Master-Details widgets described in Table 8-1.

    If you want to modify the UI components in the default forms or tables, see Chapter 6, "Creating a Basic Page" or Chapter 7, "Adding Tables".


    Tip:

    Accessor returns can be collections or single objects. Single objects can be displayed only in forms. Consequently, the master-detail widgets available from the Data Control Palette context menu differ depending on whether the accessor return is a collection or a single object.

Table 8-1 Master-Detail Widgets

Component Description

ADF Master Table, Detail Form

Displays a master object in a table and the detail object in a read-only form under the table. By default, the table or form provides range navigation for scrolling through the data if the objects are collections. When a specific data object is selected in the master table, the first related detail data object is displayed in the form below it. The user must use the form navigation to scroll through each subsequent detail data objects.

ADF Master Form, Detail Table

Displays the master object in a read-only form and the detail object in a read-only table under the form. By default the table or form provides navigation for scrolling through the data if the objects are collections. When a specific master data object is displayed in the form, the related detail data objects are displayed in a table below it.

Note: This widget is available only when both the master and detail objects are collections.

ADF Master Form, Detail Form

Displays the master and detail objects in separate forms. By default, the forms provide range navigation for scrolling through the data if the objects are collections. When a specific master data object is displayed in the top form, the first related detail data object is displayed in the form below it. The user must use the form navigation to scroll through each subsequent detail data object.

ADF Master Table, Detail Table

Displays the master and detail objects in separate tables. By default the tables provide range navigation for scrolling through the data. When a specific master data object is selected in the top table, the first set of related detail data objects are displayed in the table below it.

Note: This widget is available only when both the master and detail objects are collections.


8.3.2 What Happens When You Create a Master-Detail Browse Page

When you drag and drop from the Data Control Palette, JDeveloper does many things for you, including adding code to the JSF page and corresponding entries in the page definition file. For a full description of what happens and what is created when you use the Data Control Palette, see Section 5.2.3, "What Happens When You Create a Component From the Data Control Palette".

8.3.2.1 Code Generated in the JSF Page

Example 8-1 shows the code generated in a JSF page when an accessor return that is a collection is dropped on a JSF page. This sample page displays the service history for each service request. The page was creating by dropping the serviceHistoryCollection accessor return, which is a detail collection under the ServiceRequest method return on the page as an ADF Master Form, Detail Table.

Example 8-1 Code Generated for a Master-Detail Form and Table

<af:panelGroup layout="vertical">
  <af:panelHeader text="oracle.srdemo.model.ServiceRequest">
    <af:panelForm>
      <af:inputText value="#{bindings.assignedDate.inputValue}"
                    label="#{bindings.assignedDate.label}"
                    readOnly="true"/>
      <af:inputText value="#{bindings.problemDescription.inputValue}"
                    label="#{bindings.problemDescription.label}"
                    readOnly="true"/>
      <af:inputText value="#{bindings.requestDate.inputValue}"
                    label="#{bindings.requestDate.label}"
                    readOnly="true"/>
      <af:inputText value="#{bindings.status.inputValue}"
                    label="#{bindings.status.label}" readOnly="true"/>
      <af:inputText value="#{bindings.svrId.inputValue}"
                    label="#{bindings.svrId.label}" readOnly="true"/>
      <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>
      </f:facet>
    </af:panelForm>
  </af:panelHeader>
  <af:panelHeader text="serviceHistoryCollection">
    <af:table rows="#{bindings.ServiceRequestserviceHistoryCollection.rangeSize}"
              first="#{bindings.ServiceRequestserviceHistoryCollection.
                       rangeStart}"
              emptyText="#{bindings.ServiceRequestsserviceHistoryCollection.
                           viewable ? \'No rows yet.\' : \'Access Denied.\'}"
              var="row"
              value="#{bindings.ServiceRequestserviceHistoryCollection.
                       collectionModel}"
              selectionState="#{bindings.ServiceRequestserviceHistoryCollection.
                                collectionModel.selectedRow}"
              selectionListener="#{bindings.
                                   ServiceRequestserviceHistoryCollection.
                                   collectionModel.makeCurrent}">
      <f:facet name="selection">
        <af:tableSelectOne text="Select and ">
          <af:commandButton text="Submit"/>
        </af:tableSelectOne>
      </f:facet>
      <af:column headerText="#{bindings.ServiceRequestserviceHistoryCollection.
                               labels.lineNo}"
                 sortable="true" sortProperty="lineNo">
        <af:outputText value="#{row.lineNo}">
          <f:convertNumber groupingUsed="false"
                           pattern="#{bindings.
                                      ServiceRequestserviceHistoryCollection.
                                      formats.lineNo}"/>
        </af:outputText>
      </af:column>
      <af:column headerText="#{bindings.ServiceRequestserviceHistoryCollection.
                               labels.nextLineItem}"
                 sortable="true" sortProperty="nextLineItem">
        <af:outputText value="#{row.nextLineItem}">
          <f:convertNumber groupingUsed="false"
                           pattern="#{bindings.
                                      ServiceRequestserviceHistoryCollection.
                                      formats.nextLineItem}"/>
        </af:outputText>
      </af:column>
      <af:column headerText="#{bindings.ServiceRequestserviceHistoryCollection.
                               labels.notes}"
                 sortable="true" sortProperty="notes">
        <af:outputText value="#{row.notes}"/>
      </af:column>
      <af:column headerText="#{bindings.ServiceRequestserviceHistoryCollection.
                               labels.svhDate}"
                 sortable="true" sortProperty="svhDate">
        <af:outputText value="#{row.svhDate}">
          <f:convertDateTime pattern="#{bindings.
                                        ServiceRequestserviceHistoryCollection.
                                        formats.svhDate}"/>
        </af:outputText>
      </af:column>
      <af:column headerText="#{bindings.ServiceRequestserviceHistoryCollection.
                               labels.svhType}"
                 sortable="true" sortProperty="svhType">
        <af:outputText value="#{row.svhType}"/>
      </af:column>
    </af:table>
  </af:panelHeader>
</af:panelGroup>

In the example, the form, which displays the master collection, is just like a simple read-only form (for more information, see Section 6.3.2, "What Happens When You Use the Data Control Palette to Create a Form"), except that it includes an ADF Faces panelHeader component, which by default contains the fully qualified name of the master data object populating the form. You can change this label as needed.

Each text field in the form contains an EL expression that references a specific attribute binding defined in the page definition file. As you will see in Section 8.3.2.2, "Binding Objects Added to the Page Definition File", the attribute bindings in the page definition file reference an iterator that retrieves the data from the collection, which populates the text fields.

Because the object displayed in the form is a collection, by default the form includes four range navigation buttons: First, Previous, Next, and Last. These buttons enable the user to scroll through the data objects in the collection.

The actionListener of each button is bound to a data control operation, which performs the navigation. The execute property used in the actionListener binding, invokes the operation when the button is clicked. (If the form displayed a single object, JDeveloper would have omitted the range navigation components.)

The table, which displays the detail data collection, is the same as a simple table (for more information, see Section 7.2.2, "What Happens When You Use the Data Control Palette to Create a Table"). Like the form, the table provides a panelHeader, which defaults to the name of the detail data object. The table tag attributes are bound to the ServiceRequestserviceHistoryCollection table binding object in the page definition file. As you will see later, in the page definition file, the table binding object references the iterator that retrieves the data from the collection to populate the table.

By default, the table includes a tableSelectOne selection facet and a submit button that enables the user to select a specific object in the collection. The default button is not bound to a method. So to get the selection facet to work, you would need to add an action binding to the button. For example, you could bind the button to a method that enables the user to edit the selected data object, as was done in the SRMain page of the SRDemo application. For more information about selection facets, see Section 7.6, "Enable Row Selection in a Table".

8.3.2.2 Binding Objects Added to the Page Definition File

Example 8-2 shows the page definition file created for the master-detail page. The executables element contains a method iterator binding object for the service requests (which is the master collection) and an accessor iterator binding for the service request history (which is the detail collection). The MasterBinding attribute in the accessor iterator binding references the method iterator for the master collection. This enables the detail iterator to display the correct detail data objects for the current master object (for more information, see Section 8.3.3, "What Happens at Runtime").

The bindings element contains a methodAction object, which invokes the method iterator for the master collection, and the value bindings for the form and the table.

The attribute bindings that populate the text fields in the form are defined in the attributeValues elements. The id contains the name of each attribute, and the IterBinding references the method iterator that retrieves the data from the master collection to populate the text fields.

The range navigation buttons in the form are bound to the action bindings defined in the action elements. As in the attribute bindings, the IterBinding attribute of the action binding references the iterator for the master collection.

The table, which displays the detail data, is bound to the table binding defined in the table element. The IterBinding attribute references the iterator for the detail collection.

For more information about the elements and attributes of the page definition file, see Section A.7, "<pageName>PageDef.xml".

Example 8-2 Binding Objects Added to the Page Definition for a Master-Detail Page

<executables>
    <methodIterator id="findAllServiceRequestIter"
                    Binds="findAllServiceRequest.result"
                    DataControl="SRPublicFacade" RangeSize="10"
                    BeanClass="oracle.srdemo.model.ServiceRequest"/>
    <accessorIterator id="serviceHistoryCollectionIterator" RangeSize="10"
                      Binds="serviceHistoryCollection"
                      DataControl="SRPublicFacade"
                      BeanClass="oracle.srdemo.model.ServiceHistory"
                      MasterBinding="findAllServiceRequestIter"/>
</executables>
<bindings>
    <methodAction id="findAllServiceRequest"
                  InstanceName="SRPublicFacade.dataProvider"
                  DataControl="SRPublicFacade"
                  MethodName="findAllServiceRequest" RequiresUpdateModel="true"
                  Action="999"
                  ReturnName="SRPublicFacade.methodResults.SRPublicFacade_
                               dataProvider_findAllServiceRequest_result"/>
    <attributeValues id="assignedDate" IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="assignedDate"/>
      </AttrNames>
    </attributeValues>
    <attributeValues id="problemDescription"
                     IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="problemDescription"/>
      </AttrNames>
    </attributeValues>
    <attributeValues id="requestDate" IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="requestDate"/>
      </AttrNames>
    </attributeValues>
    <attributeValues id="status" IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="status"/>
      </AttrNames>
    </attributeValues>
    <attributeValues id="svrId" IterBinding="findAllServiceRequestIter">
      <AttrNames>
        <Item Value="svrId"/>
      </AttrNames>
    </attributeValues>
    <action id="First" RequiresUpdateModel="true" Action="12"
            IterBinding="findAllServiceRequestIter"/>
    <action id="Previous" RequiresUpdateModel="true" Action="11"
            IterBinding="findAllServiceRequestIter"/>
    <action id="Next" RequiresUpdateModel="true" Action="10"
            IterBinding="findAllServiceRequestIter"/>
    <action id="Last" RequiresUpdateModel="true" Action="13"
            IterBinding="findAllServiceRequestIter"/>
    <table id="ServiceRequestserviceHistoryCollection"
           IterBinding="serviceHistoryCollectionIterator">
      <AttrNames>
        <Item Value="lineNo"/>
        <Item Value="nextLineItem"/>
        <Item Value="notes"/>
        <Item Value="svhDate"/>
        <Item Value="svhType"/>
      </AttrNames>
    </table>
</bindings>

8.3.3 What Happens at Runtime

As was previously mentioned in Section 5.5.2.2, "About Bindings in the executables Element", ADF iterators are associated with RowSetIterator objects, which manage which data objects, or rows, are currently displayed in the UI. At runtime, the rowset iterators manage the data displayed in the master and detail widgets.

Both the master and detail rowset iterators listen to rowset navigation events, such as the user selecting a specific row or clicking the range navigation buttons, and display the appropriate rows in the UI. In the case of the default master-detail widgets, the rowset navigation events are the command buttons on a form (First, Previous, Next, Last) or the selection facet on a table.

The rowset iterator for the detail collection manages the synchronization of the detail data with the master data. It listens to the row navigation events in both the master and detail collections. The MasterBinding attribute on the detail iterator definition in the page definition file tells the detail rowset iterator which master iterator to listen to. If a rowset navigation event occurs in the master collection, the detail rowset iterator automatically executes and returns the detail rows related to the current master row.

8.3.4 Displaying Master-Detail Data on Separate Browse Pages

The default master-detail widgets display the master-detail data on a single page. However, using the master and detail objects on the Data Control Palette, you can also display the collections on separate pages, and still have the binding iterators manage the synchronization of the master and detail objects.

For example, in the SRDemo application the service requests and service request history are displayed on the SRMain page. However, the page could display the service request only, and instead of showing the service request history, it could provide a button called Details. If the user clicks the Details button, the application would navigate to a new page that displays all the related service request history in a table. A button on the service request history page would enable the user to return to the service request page.

To display master-detail objects on separate pages, create separate pages for the master and detail objects using the individual table or form components available from the Data Control Palette. (For information about using the form or table components provided by the Data Control Palette, see Chapter 6, "Creating a Basic Page" or Chapter 7, "Adding Tables".) Remember that the detail object iterator manages the synchronization of the master and detail data. So, be sure to drag the appropriate detail object from the Data Control Palette, when you create the page to display the detail data (see Figure 8-1).

To handle the page navigation, add buttons to each page. Each button must specify a navigation rule outcome value in the action attribute. In the faces-config.xml file, add a navigation rule from the master data page to the detail data page, and another rule to return from the detail data page to the master data page. The from-outcome value in the navigation rules must match the outcome value specified in the action attribute of the buttons. For information about adding navigation between pages, see Chapter 9, "Adding Page Navigation Using Outcomes".