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

8 Displaying Master-Detail Data

This chapter describes how to create various types of pages that display master-detail related data.

This chapter includes the following sections:

For information about using a selection list to populate a collection with a key value from a related master or detail collection, see Section 11.7, "Creating Databound Dropdown Lists".

8.1 Introduction to Displaying Master-Detail Data

In ADF, a master-detail relationship refers to two data objects in the data control hierarchy that are logically related in such a way that an instance of one object automatically contains a related instance of the other object. For example, in the SRDemo application, when a data control method returns a collection of service requests, each service-request object contains a list of related service-history objects. The service-history objects are returned by an accessor that is a child of the parent method in the data control hierarchy. Usually, a master-detail relationship in the data control is established by one or more unique attributes that both objects share or by an object hierarchy. For example, in the SRDemo application the serviceRequest collection and the serviceHistoryCollection have a master-detail relationship, because both collections contain the svrId attribute (the service request number). You can also have master-detail relationships between collections and single objects. For example, each object in a collection of service requests could contain a single user object to which that service request is assigned.

Tip:

In TopLink and traditional relational databases master-detail relationships are called foreign-key relationships.

When objects have a master-detail relationship, you can declaratively create pages that display the data from both objects simultaneously. For example, the SRDemo application has a page that displays a service request in a form at the top of the page and its related service history in a table at the bottom of the page.

This is possible because the service request and service history objects have a master-detail relationship. In this example, the service request is the master object and the service history is the detail object. The ADF iterators automatically manage the synchronization of the detail data objects displayed for a selected master data object.

Read this chapter to understand:

8.2 Identifying Master-Detail Objects on the Data Control Palette

JDeveloper enables you to declaratively create master-detail pages using the Data Control Palette. The Data Control Palette displays master-detail related objects in a hierarchy, where the detail object is displayed as an accessor return under the master object. In the data control, accessor returns are always detail objects in a master-detail relationship.

Figure 8-1 shows the Data Control Palette for the SRDemo application. Because the serviceHistory and ServiceRequest objects have a master-detail relationship, the accessor return serviceHistoryCollection appears under the ServiceRequest method return. In this case, the accessor return is a collection of service history objects related to a service request object. Method returns are always collections, but accessor returns can be either collections or single objects.

Tip:

By default, when data controls are created from TopLink POJOs (or session beans over POJOs), the names of accessor returns that are collections end in Collection. For example, serviceHistoryCollection.

Figure 8-1 Master-Detail Objects on the Data Control Palette

Master-Detail Collections on Data Control Palette

Tip:

The master-detail hierarchy displayed in the Data Control Palette does not reflect the cardinality of the relationship (for example, one-to-many, one-to-one, many-to-many). The hierarchy simply shows which collection (the master) is being use to retrieve one or more objects from another collection (the detail).

When creating a page that displays master-detail objects, be sure to correctly identify which object is the master and which is the detail for your particular purposes. Otherwise, you may not display the desired data on the page.

For example, if you want to display a user and all the related expertise areas to which the user is assigned, then User would be the master object. However, if you wanted to display an expertise area and all the users is assigned to it, then expertiseArea would be the master object. The detail objects displayed on a page depend on which object is the master.

Tip:

In the Data Control Palette, the attributes shared by both the master and detail objects appear under only one of the objects, not both. For example, in the SRDemo application Data Control Palette, the srvId attribute appears under the ServiceRequest master node, but not the serviceHistoryCollection detail node.

Also, in some cases, the master collection appears as an accessor return under a detail collection. For example, in Figure 8-1, ServiceRequest, which is a master collection, appears as an accessor return under the serviceHistoryCollection node, which is a detail collection. In this case, the common attribute shared by these collections creates a recursive relationship in the data control. In most cases, you would never use the accessor return that appears as a result of such a recursion to create a UI component.

For more information about the icons displayed on the Data Control Palette, see Section 5.2.1, "How to Understand the Items on the Data Control Palette".

8.3 Using Tables and Forms to Display Master-Detail Objects

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 pre-built master-detail widgets that display both the master and detail objects on the same page as any combination of read-only tables and forms. All you have to do is drop the detail collection on the page and choose the type of widget you want to use.

The pre-built master-detail widgets available from the Data Control Palette include range navigation that enables the user to scroll through the data objects in collections. The the table provided by the pre-built master-detail widgets includes a selection facet and Submit command button. By default, all attributes of the master and detail objects are included in the master-detail widgets as text fields (in forms) or columns (in tables). You can delete unwanted attributes by removing the text field or column from the page.

Tip:

If you do not want to use the pre-built master-detail widgets, you can drag and drop the master and detail objects individually as tables and forms on a single page or on separate pages. For more information about creating individual forms and tables, see Chapter 6, "Creating a Basic Page" or Chapter 7, "Adding Tables".

When you add master-detail components to a page, the iterator bindings are responsible for exposing data to the components on the page. The iterator bindings bind to the underlying rowset iterators. The rowset iterator for the detail object is responsible for exposing the correct detail data when a specific master object is displayed or selected on the page.

Figure 8-2 shows an example of a pre-built master-detail widget, which display a service request in a form at the top of the page and all the related service history in a table at the bottom of the page. When the user scrolls through the master data, the page automatically displays the related detail data.

Figure 8-2 Pre-Built Data Control Palette Master-Detail Widget

Default master-detail widget

8.3.1 How to Display Master-Detail Objects in Tables and Forms

The Data Control Palette enables you to create both the master and detail widgets on one page with a single declarative action using pre-built master-detail forms and tables. For information about displaying master and detail data on separate pages, see Section 8.3.4, "What You May Need to Know About Master-Detail on Separate Pages".

To create a master-detail page using the pre-built ADF master-detail forms and tables:

  1. From the Data Control Palette, locate the detail object, as was previously described in Section 8.2, "Identifying Master-Detail Objects on the Data Control Palette".

  2. Drag and drop the detail object onto the JSF page.

  3. In the context menu, choose one of the following Master-Details widgets:

    • ADF Master Table, Detail Form: Displays the master objects in a table and the detail objects in a read-only form under the table.

      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 objects in a read-only form and the detail objects in a read-only table under the form.

      When a specific master data object is displayed in the form, the related detail data objects are displayed in a table below it.

      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.

      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.

      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.

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

    Note:

    If an object is not a collection, but rather just a single data item, JDeveloper automatically excludes the range navigation from the default widget.

    Also, 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.

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

8.3.2 What Happens When You Create Master-Detail Tables and Forms

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 Use the Data Control Palette".

8.3.2.1 Code Generated in the JSF Page

The JSF code generated for a pre-built master-detail widget is basically the same as the JSF code generated when you use the Data Control Palette to create a basic read-only table or form. For more information, see Chapter 6, "Creating a Basic Page" and Chapter 7, "Adding Tables". If you are building your own master-detail widgets, you might want to consider including similar components that are automatically included in the pre-built master-detail tables and forms.

The tables and forms in the pre-built master-detail widgets include a panelHeader tag that contains the fully qualified name of the data object populating the form or table. You can change this label as needed using a string or an EL expression that binds to a resource bundle.

If there is more than one data object in a collection, a form in a pre-built master-detail widget includes four commandButton tags for range navigation: First, Previous, Next, and Last. These range navigation 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 displays a single data object, JDeveloper would automatically omit the range navigation components.) For more information about range navigation, see Section 6.4, "Incorporating Range Navigation into Forms".

By default, tables in a pre-built master-detail widget include 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 automatically bound to a method or operation. 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 10.3, "Creating Command Components to Execute Methods".

Tip:

If you drop an ADF Master Table, Detail Form or ADF Master Table, Detail Table widget on the page, the parent tag of the detail component (for example, panelForm tag or table tag) automatically has the partialTriggers attribute set to the id of the master component. At runtime, the partialTriggers attribute causes only the detail component to be re-rendered when the user makes a selection in the master component, which is called partial rendering. When the master component is a table, ADF uses partial rendering, because the table does not need to be re-rendered when the user simply makes a selection in the facet: only the detail component needs to be re-rendered to display the new data. For more information about partial rendering, see Section 11.4, "Enabling Partial Page Rendering".

8.3.2.2 Binding Objects Defined in the Page Definition File

Example 8-1 shows the page definition file created for a master-detail page that was created 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.

The executables element defines a method iterator for the service requests (which is the master object) and an accessor iterator for the service history (which is the detail object). The accessor iterator contains a MasterBinding attribute, which references the method iterator for the master object. This reference to the master iterator enables the detail iterator to expose the correct detail data for the current master object (for more information, see Section 8.3.3, "What Happens at Runtime").

The bindings element defines 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 attribute of the attributeValues element contains the name of each data attribute, and the IterBinding attribute references an iterator binding to display data from the master object in the text fields.

The attribute bindings that populate the text fields in the form are defined in the attributeValues elements. The id attribute of the attributeValues element contains the name of each data attribute, and the IterBinding attribute references an iterator binding to display data from the master object in 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 binding for the master object.

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

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

Example 8-1 Binding Objects Defined in 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>
    <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"/>
        ...
      </AttrNames>
    </table>
</bindings>

8.3.3 What Happens at Runtime

As was previously mentioned in Section 5.5.2.2, "Binding Objects Defined in the executables Element", ADF iterators are associated with underlying RowSetIterator objects, which manage which data objects, or rows, are currently displayed on a page. At runtime, the rowset iterators manage the data displayed in the master and detail components.

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 components, the rowset navigation events are the command buttons on a form (First, Previous, Next, Last) or the selection facet and Submit button 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 What You May Need to Know About Master-Detail on Separate Pages

The default master-detail components 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 history are displayed on the SRMain page. However, the page could display the service request only, and instead of showing the service 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 history in a table. A button on the service history page would enable the user to return to the service request page.

To display master-detail objects on separate pages, create two pages, one for the master object and one for the detail object, using the individual tables or forms available from the Data Control Palette. (For information about using the forms or tables, 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 Section 8.2, "Identifying Master-Detail Objects on the Data Control Palette").

To handle the page navigation, add command buttons or links to each page, or use the default Submit button available when you create a form or table using the Data Control Palette. 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".

8.4 Using Trees to Display Master-Detail Objects

In addition to tables and forms, you can also display master-detail data in hierarchical trees. The ADF Faces tree component, available from the Data Control Palette, can display multiple root nodes that are populated by a binding on a master object. Each root node in the tree may have any number of branches, which are populated by bindings on detail objects. A tree can have multiple levels of nodes, each representing a detail object of the parent node. Each node in the tree is indented to show its level in the hierarchy.

The tree component includes mechanisms for expanding and collapsing the tree nodes; however, it does not have focusing capability. If you need to use focusing, consider using the ADF Faces TreeTable component (for more information, see Section 8.5, "Using Tree Tables to Display Master-Detail Objects"). By default, the icon for each node in the tree is a folder; however, you can use your own icons for each level of nodes in the hierarchy.

Figure 8-3 shows an example of a tree from the SRManage page of the SRDemo application. The tree displays two levels of nodes: staff members and service requests assigned to them. The root nodes display staff members. The branch nodes display open or pending service requests assigned to each staff member.

Figure 8-3 Databound ADF Faces Tree

ADF Tree Component

8.4.1 How to Display Master-Detail Objects in Trees

A tree consists of a hierarchy of nodes, where each subnode is a branch off a higher level node. Each node level in a databound ADF Faces tree is populated by a different data collection. In JDeveloper, you define a databound tree using the Tree Binding Editor, which enables you to define the rules for populating each node level in the tree. There must be one rule for each node level in the hierarchy. Each rule defines the following node level properties:

  • The data collection that populates that node level

  • The attributes from the data collection that are displayed at that node level

  • An accessor method that returns a detail object to be displayed as a branch of the current node level

To display master-detail objects in a tree:

  1. Drag the master object from the Data Control Palette, and drop it onto the page. This should be the master data that will represent the root level of the tree.

    Note:

    The root node must be a collection represented by a method return or accessor return. You cannot use a single-object accessor return as the root node of a tree.
  2. In the context menu, choose Trees > ADF Tree.

    JDeveloper displays the Tree Binding Editor, as shown in Figure 8-4.

    Figure 8-4 Tree Binding Editor, Edit Rule Tab

    Tree Binding Editor, Edit Rule Tab
  3. In the Edit Rule page of the Tree Binding Editor, define a rule for each node level that you want to appear in the tree. To define a rule you must select the following items:

    • Data Collection Definition: Select the data collection that will populate the node level you are defining.

      The first rule defines the root node level. So, for the first rule, select the same collection that you dragged from the Data Control Palette to create the tree, which was a master collection.

      To create a branch node, select the appropriate detail collection. For example, to create a root node of users, you would select the User collection for the first (root node) rule; to create a branch that displays services requests, you would select the ServiceRequest collection in the branch rule.

    • Display Attribute: Select one or more attributes to display at each node level. For example, for a node of users, you might select both the FirstName and LastName attributes.

    • Branch Rule Accessor: Select the accessor method that returns the detail collection that you want to appear as a branch under the node level you are defining. The list displays only the accessor methods that return the detail collections for the master collection you selected for the rule. If you choose <none>, the node will not expand to display any detail collections, thus ending the branch. For example, if you are defining the User node level and you want to add a branch to the service requests for each user, you would select the accessor method that returns the service request collection. Then, you must define a new rule for the serviceRequest node level.

    • Polymorphic Restriction: Optionally, you can define a node-populating rule for an attribute whose value you want to make a discriminator. The rule will be polymorphic because you can define as many node-populating rules as desired for the same attribute, as long as each rule specifies a unique discriminator value. The tree will display a separate branch for each polymorphic rule, with the node equal to the discriminator value of the attribute.

      Tip:

      Be sure to click Add New Rule after you define each rule. If you click OK instead, the last rule you defined will not be saved. When you click Add New Rule, JDeveloper displays the Show Rules tab of the Tree Binding Editor, where you can verify the rules you have created.
  4. Use the Show Rules page of the Tree Binding Editor, shown in Figure 8-5, to:

    • Change the order of the rules

      The order of the rules should reflect the hierarchy that you want the tree to display.

    • Delete rules

    Note:

    You cannot change the icon displayed in an ADF Faces or JSF tree component.

    The first rule listed in the Show Rules page of the Tree Binding Editor, populates the root node level of the tree. So, be sure that the first rule populates the logical root node for the tree, depending on the structure of your data model.

    For example, in the sample tree previously shown in Figure 8-3, the first rule would be the one that populates the user nodes. The order of the remaining rules should follow the hierarchy of the nodes you want to display in the tree.

Figure 8-5 Tree Binding Editor, Show Rule Tab

Tree Binding Editor, Show Rule Tab

8.4.2 What Happens When You Create ADF Databound Trees

When you drag and drop from the Data Control Palette, JDeveloper does many things for you. 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 Use the Data Control Palette".

When you create a databound tree using the Data Control Palette, JDeveloper adds binding objects to the page definition file, and it also adds the tree tag to the JSF Page. The resulting UI component is fully functional and does not require any further modification.

8.4.2.1 Code Generated in the JSF Page

Example 8-2 shows the code generated in a JSF page when you use the Data Control Palette to create a tree. This sample tree displays two levels of nodes: users and service requests. The User collection is the root node and is returned by the findAllStaff method.

Example 8-2 Code Generated in the JSF Page for a Databound Tree

<h:form>
   <af:tree value="#{bindings.findAllStaff1.treeModel}" var="node">
      <f:facet name="nodeStamp">
         <af:outputText value="#{node}"/>
      </f:facet>
   </af:tree>
</h:form>

By default, the af:tree tag is created inside a form. The value attribute of the tree tag contains an EL expression that binds the tree component to the findAllStaff1 tree binding object in the page definition file. The treeModel property refers to an ADF class that defines how the tree hierarchy is displayed, based on the underlying data model. The var attribute provides access to the current node.

In the f:facet tag, the nodeStamp facet is used to display the data for each node. Instead of having a component for each node, the tree repeatedly renders the nodeStamp facet, similar to the way rows are rendered for the ADF Faces table component.

The ADF Faces tree component uses an instance of the oracle.adf.view.faces.model.PathSet class to display expanded nodes. This instance is stored as the treeState attribute on the component. You may use this instance to programmatically control the expanded or collapsed state of an element in the hierarchy. Any element contained by the PathSet instance is deemed expanded. All other elements are collapsed.

8.4.2.2 Binding Objects Defined in the Page Definition File

Example 8-3 shows the binding objects defined in the page definition file for the ADF databound tree.

Example 8-3 Binding Objects Defined the Page Definition File for a Databound Tree

<executables>
    <methodIterator id="findAllStaffIter" Binds="findAllStaff.result"
                    DataControl="SRPublicFacade" RangeSize="10"
                    BeanClass="oracle.srdemo.model.entities.User"/>
</executables>
<bindings>
    <methodAction id="findAllStaff" InstanceName="SRPublicFacade.dataProvider"
                  DataControl="SRPublicFacade" MethodName="findAllStaff"
                  RequiresUpdateModel="true" Action="999"
                  ReturnName="SRPublicFacade.methodResults.
                                SRPublicFacade_dataProvider_findAllStaff_result"/>
    <tree id="findAllStaff1" IterBinding="findAllStaffIter">
      <AttrNames>
        <Item Value="city"/>
        <Item Value="countryId"/>
        <Item Value="email"/>
        <Item Value="firstName"/>
        <Item Value="lastName"/>
        <Item Value="postalCode"/>
        <Item Value="stateProvince"/>
        <Item Value="streetAddress"/>
        <Item Value="userId"/>
        <Item Value="userRole"/>
      </AttrNames>
      <nodeDefinition DefName="oracle.srdemo.model.entities.User" id="UserNode">
        <AttrNames>
          <Item Value="firstName"/>
          <Item Value="lastName"/>
        </AttrNames>
        <Accessors>
          <Item Value="assignedToCollection"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="oracle.srdemo.model.entities.ServiceRequest"
                      id="ServiceRequestNode">
        <AttrNames>
          <Item Value="problemDescription"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
</bindings>

The page definition file contains the rule information defined in the Tree Binding Editor. In the executables element, notice that although the tree displays two levels of nodes, only one iterator binding object is needed. This iterator iterates over the master collection, which populates the root nodes of the tree. The accessor you specified in the node rules return the detail data for each branch node.

In the example, the iterator happens to be a method iterator because a method return was dragged from the Data Control Palette and dropped on the page as an ADF tree. If an accessor return had been dragged from the Data Control Palette, this would be an accessor iterator instead of a method iterator. Because a method iterator is used in this example, a corresponding methodAction is defined in the bindings element. The methodAction encapsulates the details about how to invoke the method, which returns the data collection.

The tree element is the value binding for all the attributes displayed in the tree. The iterBinding attribute of the tree element references the iterator binding that populates the data in the tree. The AttrNames element within the tree element defines binding objects for all the attributes in the master collection. However, the attributes that you select to appear in the tree are defined in the AttrNames elements within the nodeDefinition elements.

The nodeDefinition elements define the rules for populating the nodes of the tree. There is one nodeDefinition element for each node, and each one contains the following attributes and subelements:

  • DefName: An attribute that contains the fully qualified name of the data collection that will be used to populate the node.

  • id: An attribute that defines the name of the node.

  • AttrNames: A subelement that defines the attributes that will be displayed in the node at runtime.

  • Accessors: A subelement that defines the accessor method that returns the next branch of the tree.

The order of the nodeDefintion elements within the page definition file defines the order or level of the nodes in the tree, were the first nodeDefinition element defines the root node. Each subsequent nodeDefinition element defines a sub-node of the one before it.

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

8.4.3 What Happens at Runtime

Tree components use oracle.adf.view.faces.model.TreeModel to access data. This class extends CollectionModel, which is used by the ADF Faces table component to access data. For more information about the TreeModel class, refer to the ADF Faces Javadoc.

When a page with a tree is displayed, the iterator binding on the tree populates the root nodes. When a user collapses or expands a node to display or hide its branches, a DisclosureEvent event is sent. The isExpanded method on this event determines whether the user is expanding or collapsing the node. The DisclosureEvent event has an associated listener.

The DisclosureListener attribute on the tree is bound to the accessor method specified in the node rule defined in the page definition file. This accessor method is invoked in response to the DisclosureEvent event; in other words, whenever a user expands the node the accessor method populates the branch nodes.

8.5 Using Tree Tables to Display Master-Detail Objects

Use the ADF Faces treeTable component to display a hierarchy of master-detail collections in a table. The advantage of using a treeTable component rather than a tree component is that the treeTable component provides a mechanism that enables users to focus the view on a particular node in the tree.

Figure 8-6 shows an example of a tree table that displays three levels of nodes: users, service requests, and service history. Each root node represents an individual user. The branches off the root nodes display the service requests associated with that user. Each service request node branches to display the service history for each service request.

Figure 8-6 Databound ADF Faces Tree Table

ADF Faces Tree Table

A databound ADF Faces treeTable displays one root node at a time, but provides navigation for scrolling through the different root nodes. Each root node can display any number of branch nodes. Every node is displayed in a separate row of the table, and each row provides a focusing mechanism in the leftmost column.

The ADF Faces treeTable component includes the following built-in functionality:

8.5.1 How to Display Master-Detail Objects in Tree Tables

The steps for creating an ADF Faces databound tree table are exactly the same as those for creating an ADF Faces databound tree, except that you drop the data collection as an ADF Tree Table instead of an ADF Tree. For more information, see Section 8.4.1, "How to Display Master-Detail Objects in Trees".

8.5.2 What Happens When You Create a Databound Tree Table

When you drag and drop from the Data Control Palette, JDeveloper does many things for you. 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 Use the Data Control Palette".

When you create a databound tree table using the Data Control Palette, JDeveloper adds binding objects to the page definition file, and it also adds the treeTable tag to the JSF Page. The resulting UI component is fully functional and does not require any further modification.

8.5.2.1 Code Generated in the JSF Page

Example 8-4 shows the code generated in a JSF page when you use the Data Control Palette to create a tree table. This sample tree table displays three levels of nodes: users, service requests, and service history.

By default, the treeTable tag is created inside a form. The value attribute of the tree table tag contains an EL expression that binds the tree component to the binding object that will populate it with data, which in the example is the findAllStaff1 tree binding object. The treeModel property refers to an ADF class that defines how the tree hierarchy is displayed, based on the underlying data model. The var attribute provides access to the current node.

Example 8-4 Code Generated in the JSF Page for a Databound ADF Faces Tree Table

<h:form>
  <af:treeTable value="#{bindings.findAllStaff1.treeModel}" var="node">
    <f:facet name="nodeStamp">
      <af:column>
        <af:outputText value="#{node}"/>
      </af:column>
    </f:facet>
    <f:facet name="pathStamp">
      <af:outputText value="#{node}"/>
    </f:facet>
  </af:treeTable>
</h:form>

In the facet tag, the nodeStamp facet is used to display the data for each node. Instead of having a component for each node, the tree repeatedly renders the nodeStamp facet, similar to the way rows are rendered for the ADF Faces table component. The pathStamp facet renders the column and the path links above the table that enable the user to return to the parent node after focusing on a detail node.

8.5.2.2 Binding Objects Defined in the Page Definition File

The binding objects created in the page definition file for a tree table are exactly the same as those created for a tree. For more information about tree binding objects, see Section 8.4.2.2, "Binding Objects Defined in the Page Definition File".

8.5.3 What Happens at Runtime

Tree components use oracle.adf.view.faces.model.TreeModel to access data. This class extends CollectionModel, which is used by the ADF Faces table component to access data. For more information about the TreeModel class, refer to the ADF Faces Javadoc.

When a page with a tree table is displayed, the iterator binding on the treeTable component populates the root node and listens for a row navigation event (such as the user clicking the Next or Previous buttons or selecting a row from the range navigator). When the user initiates a row navigation event, the iterator displays the appropriate row.

If the user changes the view focus (by clicking on the component's focus icon), the treeTable component generates a focus event (FocusEvent). The node to which the user wants to change focus is made the current node before the event is delivered. The treeTable component then modifies the focusPath property accordingly. You can bind the FocusListener attribute on the tree to a method on a managed bean. This method will then be invoked in response to the focus event.

When a user collapses or expands a node, a disclosure event (DisclosureEvent) is sent. The isExpanded method on the disclosure event determines whether the user is expanding or collapsing the node. The disclosure event has an associated listener, DisclosureListener. The DisclosureListener attribute on the tree table is bound to the accessor method specified in the node rule defined in the page definition file. This accessor method is invoked in response to a disclosure event (for example, the user expands a node) and returns the collection that populates that node.

The treeTable component includes Expand All and Collapse All links. When a user clicks one of these links, the treeTable sends a DisclosureAllEvent event. The isExpandAll method on this event determines whether the user is expanding or collapsing all the nodes. The table then expands or collapses the nodes that are children of the root node currently in focus. In large trees, the expand all command will not expand nodes beyond the immediate children. The ADF Faces treeTable component uses an instance of the oracle.adf.view.faces.model.PathSet class to determine expanded nodes. This instance is stored as the treeState attribute on the component. You can use this instance to programmatically control the expanded or collapsed state of a node in the hierarchy. Any node contained by the PathSet instance is deemed expanded. All other nodes are collapsed. This class also supports operations like addAll() and removeAll().

Like the ADF Faces table component, a treeTable component provides for range navigation. However, instead of using the rows attribute, the treeTable component uses a rowsByDepth attribute whose value is a space-separated list of non-negative numbers. Each number defines the range size for a node level on the tree. The first number is the root node of the tree, and the last number is for the branch nodes. If there are more branches in the tree than numbers in the rowsByDepth attribute, the tree uses the last number in the list for the remaining branches. Each number defines the limit on the number items displayed at one time in each branch. If you want to display all items in a branch, specify 0 in that position of the list.

For example, if the rowsByDepth attribute is set to 0 0 3, all root nodes will be displayed, all direct children of the root nodes will be displayed, but only three nodes will display per branch after that. The treeTable component includes links to navigate to additional nodes, enabling the user to display the additional nodes.

For more information about the ADF Faces TreeTable component, refer to the oracle.adf.view.faces.component.core.data.CoreTreeTable class in the ADF Faces Javadoc.

8.6 Using an Inline Table to Display Detail Data in a Master Table

As you may recall from Section 7.5, "Adding Hidden Capabilities to a Table", you can use the detailStamp facet in a table to hide or show additional information about a specific data object displayed in the table. When you add a component to this facet, the table displays an additional column labeled Details, which displays the additional information. It includes a toggle mechanism that enables the user to hide or show the information displayed in the Details column in a manner similar to the mechanism in an ADF Faces tree or treeTable component. In the case described in Section 7.5, "Adding Hidden Capabilities to a Table", the additional information was a single attribute from the same data collection that populates the table.

Using master-detail collections on the Data Control Palette, you can declaratively add an inline table to the detailStamp facet that displays additional information from a detail collection. A master collection is used to populate the main table and a detail collection is used to populate the inline table.

Figure 8-7 shows how an inline table of service requests can be embedded in a table of service request staff. If the user clicks the Show link in the Details column, which is built into the table facet, an inline table of service requests is displayed under the selected row of the table. The main table is populated by a master collection of users and displays the user's first and last name. The inline table is populated by a detail collection of service requests and displays the service request problem description and status.

Figure 8-7 Inline Table Displaying Information from a Detail Collection

Master Table, Inline Detail Table

8.6.1 How to Display Detail Data Using an Inline Table

Using the Data Control Palette, you can create both the main table and the inline table in a single declarative action. Since an inline table is similar to a tree table, you use the Tree Binding Editor to define the rules that populate the main table and the inline detail table. There must be one rule for the main table and one rule for the inline detail table. Each rule defines the following properties:

  • The data collection that populates the table

  • The attributes from the data collection that are displayed in the table

The rule for the main table must also specify an accessor method that returns the detail collection that will populate the inline table.

To create a master table with an inline detail table:

  1. Drag a master data object from the Data Control Palette, and drop it on the page. This should be the master object that you want to populate the main table.

    Note:

    You cannot use a single-object accessor return to create a table.
  2. In the context menu, choose Tables > ADF Master Table, Inline Detail Table.

    JDeveloper displays the Tree Binding Editor (previously shown in Figure 8-4).

  3. In the Edit Rule page of the Tree Binding Editor, define a rule for populating the main table and another rule for populating the inline table. To define a rule you must select the following items:

    • Data Collection Definition: Select the data collection that will populate the table you are defining. The first rule defines the main table. So, for the first rule, select the same data collection that you dragged from the Data Control Palette (the master collection). When defining the rule for the inline table, select the appropriate detail collection. For example, to create a main table of users, you would select the User collection for the first rule; to create an inline table that displays service requests related to a user, you would select the ServiceRequest collection in the branch rule.

    • Display Attribute: Select one or more attributes to display in the table you are defining. Each attribute is a column in the table. For example, if the main table is displaying users, you might select both the firstName and lastName attributes.

    • Branch Rule Accessor: If you are defining the rule for the main table, select the accessor method that returns the detail collection that you want to appear in the inline detail table. The list displays only the accessor methods that return the detail collections for the master collection you selected for the rule. If you are defining the rule for the inline table, select <none>, because you cannot embed a table inside the inline table.

      Tip:

      Be sure to click the Add New Rule button after you define each rule. If you click the OK button instead, the last rule you defined will not be saved. When you click Add New Rule, JDeveloper displays the Show Rules tab of the Tree Binding Editor, where you can verify the rules you have created.
  4. Use the Show Rules page of the Tree Binding Editor, shown in Figure 8-5, to:

    • Change the order of the rules

      The rule that populates the main table must be first in the list

    • Identify the icons you want displayed for the expand and collapse mechanism

      Only the main table uses the icons, so if you want to use an icon other than the default, specify it in the rule for the main table.

      The default open icon is a solid down arrow with a minus sign, while the default closed icon is a solid right arrow with a plus sign

    • Delete rules

8.6.2 What Happens When You Create an Inline Detail Table

When you drag and drop from the Data Control Palette, JDeveloper does many things for you. 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 Use the Data Control Palette".

8.6.2.1 Code Generated in the JSF Page

When you create a master table and an inline detail table using the Data Control Palette, JDeveloper adds binding objects to the page definition file, and it also adds the table and facet to the JSF page. The resulting UI components are fully functional and do not require any further modification.

Example 8-5 shows the code generated in the JSF page. This sample displays users in the main table and service requests in the inline detail table. The User collection is returned by the findAllStaff method. The main table is defined the same as any other ADF databound table. It is bound to the findAllStaff1 binding object in the page definition file, which is a tree binding object. The columns in the main table display the user's first name and last name. The table includes a detailStamp facet in which the detail table is defined. The detail table is also bound to the findAllStaff1 tree binding object, and the columns are set up to display the data from the service request collection. As with tree components, the page definition file defines the accessor method that returns the detail collection.

Example 8-5 JSF Code Created for the Master Table with an Inline Detail Table

<af:table rows="#{bindings.findAllStaff1.rangeSize}"
          emptyText="#{bindings.findAllStaff1.viewable ? \'No rows yet.\' : 
                         \'Access Denied.\'}"
          var="row" value="#{bindings.findAllStaff1.treeModel}">
  <af:column headerText="#{bindings.findAllStaff1.labels.firstName}"
             sortable="false" sortProperty="firstName">
    <af:outputText value="#{row.firstName}"/>
  </af:column>
  <af:column headerText="#{bindings.findAllStaff1.labels.lastName}"
               sortable="false" sortProperty="lastName">
    <af:outputText value="#{row.lastName}"/>
  </af:column>
  <f:facet name="detailStamp">
    <af:table rows="#{bindings.findAllStaff1.rangeSize}"
              emptyText="No rows yet." var="detailRow"
              value="#{row.children}">
      <af:column headerText="#{row.children[0].labels.problemDescription}"
                 sortable="false" sortProperty="problemDescription">
        <af:outputText value="#{detailRow.problemDescription}"/>
      </af:column>
      <af:column headerText="#{row.children[0].labels.status}"
                 sortable="false" sortProperty="status">
        <af:outputText value="#{detailRow.status}"/>
      </af:column>
    </af:table>
  </f:facet>
</af:table>

8.6.2.2 Binding Objects Defined in the Page Definition File

Example 8-6 shows the binding objects added to the page definition file for a master table with an inline detail table. The executables element defines the findAllStaffIter iterator binding object, which iterates over the User collection that populates the main table. No iterator is needed for the detail collection, because the accessor method referenced in the tree binding object returns the detail data that is related to the currently selected master data.

In the bindings element, the methodAction binding object invokes the method that returns the User collection. The tree binding object populates the data in the master and detail tables. The nodeDefintion elements define the attributes that are displayed in the columns of the master and detail tables. The first nodeDefinition element defines the data in the master table, and the second one defines the data in the inline detail table. For more information about tree binding objects, see Section 8.4.2, "What Happens When You Create ADF Databound Trees".

Example 8-6 Binding Objects Added to the Page Definition File for a Master Table with an Inline Detail Table

<executables>
  <methodIterator id="findAllStaffIter" Binds="findAllStaff.result"
                    DataControl="SRPublicFacade" RangeSize="10"
                    BeanClass="oracle.srdemo.model.entities.User"/>
</executables>
<bindings>
  <methodAction id="findAllStaff" InstanceName="SRPublicFacade.dataProvider"
                DataControl="SRPublicFacade" MethodName="findAllStaff"
                RequiresUpdateModel="true" Action="999"
                ReturnName="SRPublicFacade.methodResults.
                            SRPublicFacade_dataProvider_findAllStaff_result"/>
  <tree id="findAllStaff1" IterBinding="findAllStaffIter">
    <AttrNames>
      <Item Value="city"/>
      <Item Value="countryId"/>
      <Item Value="email"/>
      <Item Value="firstName"/>
      <Item Value="lastName"/>
      <Item Value="postalCode"/>
      <Item Value="stateProvince"/>
      <Item Value="streetAddress"/>
      <Item Value="userId"/>
      <Item Value="userRole"/>
    </AttrNames>
    <nodeDefinition DefName="oracle.srdemo.model.entities.User" id="UserNode">
      <AttrNames>
        <Item Value="firstName"/>
        <Item Value="lastName"/>
      </AttrNames>
      <Accessors>
        <Item Value="assignedToCollection"/>
      </Accessors>
    </nodeDefinition>
    <nodeDefinition DefName="oracle.srdemo.model.entities.ServiceRequest"
                    id="ServiceRequestNode">
      <AttrNames>
        <Item Value="problemDescription"/>
        <Item Value="status"/>
      </AttrNames>
    </nodeDefinition>
  </tree>
</bindings>

8.6.3 What Happens at Runtime

When the user hides or shows the details of a row (by clicking the Hide or Show links), the table generates a DisclosureEvent event, which expands or collapses the inline detail table. The isExpanded method on this event determines whether the user is showing or hiding the detail table.

The DisclosureEvent event has an associated listener. The DisclosureListener attribute on the table is implicitly bound to the accessor method specified in the node rule defined in the page definition file. This accessor method is invoked in response to a DisclosureEvent event. For example, if the user clicks on the Show link, the accessor method is invoked to populate the data in the inline table.