Fusion Middleware Documentation
Advanced Search


Developing Web User Interfaces with Oracle ADF Faces
Close Window

Table of Contents

Show All | Collapse

21 Determining Components at Runtime

This chapter describes how to use the ADF Faces dynamicComponent with forms and tables, and how to create the AttributesModel to support it. If your application uses the full Fusion technology stack, then your model is created for you, and you can use data controls to create the dynamic component. For more information, see the "Creating a Basic Databound Page" and "Creating ADF Databound Tables" chapters of Developing Fusion Web Applications with Oracle Application Development Framework.

This chapter incudes the following sections:

21.1 About Determining Components at Runtime

There may be cases when you don't know the exact components needed at runtime. For example, your business objects may contain attributes that are only valid for certain instances, and you only want to display those attributes when necessary. Using standard components, you might need to incorporate expensive logic to determine whether or not to display certain fields.

Another example of needing a dynamic interface might be when multiple pages share the same data source. If the attributes on the object are likely to change, it would require a change to all the pages bound to it.

ADF Faces provides a dynamic component (af:dynamicComponent) that determines what components to display, and their values, at runtime. This component will only display the needed attributes for each rendered instance. Additionally, when you make changes to the associated business service, those changes will be reflected by the dynamic component, without any change needed to the UI code.

You can use the dynamic component in either a form or a table. At runtime, the needed components will be rendered within the form or table, in place of the dynamic component. The component that is rendered is based on the attribute's data type. For example, if the attribute is a String, then an inputText component is used. If it is a Date, then the inputDate component is used. Following are the components supported by the dynamic component:

  • inputText

  • inputDate

  • inputListOfValues

  • selectOneChoice

  • selectManyChoice

  • selectOneListbox

  • selectManyListbox

  • selectOneRadio

  • selectBooleanRadio

  • selectBooleanCheckbox

  • selectManyCheckbox

For a form, the dynamic component is wrapped in an iterator component. Like the collection-based components, the iterator is bound to the complete collection, in this case, a collection of attributes in the AttributesModel object. The iterator stamps through each instance on the model, copying the data for the current instance into its var attribute. The dynamic component then accesses that var value for each instance using its attributeModel attribute, and uses that information to determine the type of component to use, how to configure it, and its value.

For example, say you want to create a form that displays employee data, as shown in Figure 21-1.

Figure 21-1 Form Created with a Dynamic Component

Form created with dynamic component

You can create an AttributesModel that contains information about each of the attributes to display, as well as a way to access the values for each instance, and then create a form using just the dynamic component, as shown in Example 21-1.

Example 21-1 Dynamic Component Used in a Form

<af:panelFormLayout id="pf1">
  <af:iterator value="#{TestDynCompBean.attributesModel.attributes}" 
               var="attr" id="dyit1">
    <af:dynamicComponent value="#{TestDynCompBean.value[attr.name]}" id="dyipt3"
                         attributeModel="#{attr}"/>
  </af:iterator>
</af:panelFormLayout>

For static table, each column and the component inside that column, is statically defined in the page at design time. For a dynamic table, both the number of columns and component inside that column are dynamically defined at runtime, using the following components:

  • af:table: Defines a table, including how to get the value, using the var attribute (for more information, see Chapter 12, "Using Tables, Trees, and Other Collection-Based Components").

  • af:iterator: Defines the collection of attributes. A column, and a component in each column, will be built for each attribute.

  • af:column: Defines a column that will be stamped once for each attribute. If there are 10 attributes in the iterator, then there will be 10 columns, all stamped from this same column definition.

  • af:dynamicComponent: Defines the component for that column. The type, value, and so on, are obtained from that attribute.

Example 21-2 shows a dynamic component used in a table.

Example 21-2 Dynamic Component Used in a Table

<af:table value="#{TestDynCompBean.values}" var="row"
          varStatus="vs" rowSelection="single"
          id="t1" width="100%">
  <af:iterator value="#{TestDynCompBean.attributesModel.attributes}" id="itr1"
               var="col">
    <af:column headerText="#{col.label}" id="c1">
      <af:dynamicComponent value="#{row[col.name]}"
                           attributeModel="#{col}" id="dc1"/>
    </af:column>
  </af:iterator>
</af:table>

You can also use the dynamic component to create groupings of attributes. For example, say you are using a dynamic component to create a form that displays attributes for an employee. You want the employee information (such as first name, last name, salary, etc.) to be in one group, and you want the department information (such as department name, department number, etc.) in another group. You might create a category named "Employee Info" and another category named "Department Info." In the AttributesModel, you assign some attributes on the Employee object to be in the categories. These categories are held in the hierarchicalAttributes property of the AttributesModel object.

To create the groups on the page, you use a switcher component with two facets. The first facet will handle all attributes that belong to a category and the second will handle the "flat" group (that is, attributes that do not belong to a category).

When using groups, instead of being bound to attributes property in the AttributesModel, the main iterator is bound to hierarchicalAttributes property, which defines the root level attributes. As the iterator iterates over the collection, those category values are held in the variable named attr. In the first group, the iterator is bound to the variable attr, and so iterates through those, holding the value of the descriptors (the list of child attributes belonging in that category) in the variable nestedAttr. The child dynamic component then accesses this variable to determine the type of component and value to display for each record, as shown in Example 21-3

Example 21-3 Dynamic Component Used in Groups

<af:panelFormLayout id="pf1">
  <af:iterator value="#{TestDynCompBean.attributesModel.hierarchicalAttributes}"
               var="attr" id="dyit1">
    <af:switcher id="sw" facetName="#{attr.descriptorType}"
                 defaultFacet="ATTRIBUTE">
      <f:facet name="GROUP">
        <af:group id="gg" title="#{attr.label}">
          <af:outputText value="#{attr.label}" id="ot2"/>
          <af:iterator id="it2" value="#{attr.descriptors}" var="nestedAttr">
            <af:dynamicComponent
                 value="#{TestDynCompBean.value[nestedAttr.name]}" id="ndync1"
                 attributeModel="#{nestedAttr}" />
          </af:iterator>
        </af:group>
      </f:facet>
      <f:facet name="ATTRIBUTE">
        <af:dynamicComponent value="#{TestDynCompBean.value[attr.name]}"
                             id="iinerit1" attributeModel="#{attr}"/>
      </f:facet>
    </af:switcher>
  </af:iterator>
</af:panelFormLayout>

The main iterator iterates through the root level attributes in the attributeModel. For any root attributes that do not belong to a group (i.e. an Attribute-typed root attribute), a dynamic component is created for it in the ATTRIBUTE facet. For any root attributes that do have attributes under it (i.e. a Group-type root attribute), the attributes are added to the GROUP facet. In that facet, another iterator iterates through each regular attribute in that group, and creates a dynamic component based on each regular attribute.

21.2 Creating the Model for a Dynamic Component

The AttributesModel class is a collection of attributes, each attribute described by a BaseAttributeDescriptor object. This object provides the metadata used by the dynamic component to determine how to display the data, including the component type, the name, label, and description. You will need to extend the BaseAttributeDescriptor class and the AttributesModel class to provide the needed information for your dynamic components.

If you want to use groups with your dynamic component, then you must also create a GroupAttributeDescriptor object.

21.2.1 How to Create the Model Without Groups

To create the model, you need to create the BaseAttributeDescriptor object, the AttributesModel object, and a managed bean for the page that the dynamic component and iterator can use to access the data and metadata for the attributes.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To create the model:

  1. Create an attribute definition class that describes each property on an attribute. It is this metadata that the dynamic component will use to determine the component to use to display the data, and how to configure it. For example, this definition class might contain getter methods for the following properties on an attribute:

    • name

    • label

    • dataType

    For an example of an attribute definition class, see the TestAttributeDef inner class on the TestDynamicComponentPageDef class in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

  2. Create another class that defines each attribute, using the definition class created in Step 1. This class should define each attribute, and then return a list of attributes with that metadata populated.

    For example, an Employee object might have the following attributes:

    • Ename

    • Empno

    • Deptname

    • Deptno

    • Manager

    Example 21-4 shows how you might create an attribute definition, given the metadata created in Step 1.

    Example 21-4 Create an Attribute Definition

    public void addAttributeDef (String name, String label, Class dataType) {
      TestAttributeDef attributeDef = new TestAttributeDef(name, label, dataType);
        _attributes.put(name, attributeDef);
     }
    

    Example 21-5 then shows how to return a list of Employee object attributes with the attribute definitions populated.

    Example 21-5 Define the Attributes

    public void setupAttributes()
    {
      _attributes = new HashMap<String, TestAttributeDef>();
        addAttributeDef("Ename", "Employee Name", null, "Name", 10, 20, String.class);
        addAttributeDef("Empno", "Employee Number", null,
                          "Employee Number", 10, 20, Number.class);
       addAttributeDef("Deptname", "Department Name", null, "Department Name", 10, 20, String.class);
       addAttributeDef("Deptno", "Department Number", null, "Department Number", 10, 20, Number.class);
       addAttributeDef("Manager", "Manager", null, "Manager", 10, 20, Number.class);
    }
    

    For an example of a complete attribute definition class, see the TestDynamicComponentPageDef class in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

  3. Create a class for the BaseAttributeDescriptor object. This class must extend the BaseAttributeDescriptor class, and needs to access the metadata properties for each attribute. Example 21-6 shows how the Name and DataType might be returned.

    Example 21-6 Access the Attribute Definitions

    public class TestAttributeDescriptor extends BaseAttributeDescriptor {
      public TestAttributeDescriptor(TestAttributeDef attributeDef) {
        _attributeDef = attributeDef;
      }
     
      public Object getId() {
        return getName();
      }
     
      public String getName() {
        return _attributeDef.getName();
      }
     
      public Class getDataType() {
        return _attributeDef.getDataType();
      }
    

    For a complete example, see the TestAttributeDescriptor inner class of the TestDynamicComponentBean managed bean in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

  4. Create a class that extends the AttributesModel class. This class needs to create the attributes from the BaseAttributeDescriptor object as a list.

    Example 21-7 Create an AttributesModel

    public class TestAttributesModel extends AttributesModel {
      public TestAttributesModel() {
        _flatAttributes = new ArrayList<BaseAttributeDescriptor>();
      }
     
      public List<BaseAttributeDescriptor> getAttributes() {
        return _flatAttributes;
      private void _setupAttributesFromDefinition() {
        Map<String, List<BaseAttributeDescriptor>>();
        List<TestDynamicComponentPageDef.TestAttributeDef> attributeList = _pageDef.getAttributeDefs();
     . . .
          for (TestDynamicComponentPageDef.TestAttributeDef demoAttrDef : attributeList) {
            TestAttributeDescriptor attrDesc = new TestAttributeDescriptor(demoAttrDef);
            _flatAttributes.add(attrDesc);
            }
     
      }
    }
    

    For a complete example, see the TestAttributesModel inner class of the TestDynamicComponentBean managed bean in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

  5. Create the managed bean. The managed bean needs to return the populated AttributesModel objects for the page, as well as provide any needed logic for the page. For example, if you want to use the dynamic component in a form, and you want the user to be able to scroll to previous and next records, you need to provide that logic. Example 21-8 shows the code for returning the AttributesModel, as well as the logic for accessing next and previous records using Action events.

    Example 21-8 Managed Bean Code for a Dynamic Component

    public class TestDynamicComponentBean {
      public TestDynamicComponentBean() {
        _attrsModel = new TestAttributesModel();
      }
     
      public AttributesModel getAttributesModel() {
        return _attrsModel;
      }
     
      public Map[] getValues() {
        return _DATA;
      }
      public Map getValue() {
       return _DATA[currentRowIndex];
      }
     
      public void next(ActionEvent actionEvent) {
        if (currentRowIndex < _DATA.length - 1)
            currentRowIndex++;
      }
     
      public boolean getNextEnabled() {
        return (currentRowIndex < (_DATA.length - 1));
      }
     
      public void previous(ActionEvent actionEvent) {
        if (currentRowIndex > 0)
            currentRowIndex--;
      }
     
      public boolean getPreviousEnabled() {
        return currentRowIndex > 0;
      }
    

    For a complete example (including how the managed bean sets up the data for AttributesModel), see the TestDynamicComponentBean managed bean in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

21.2.2 How to Create the Model Using Groups

If you want to use groups with your dynamic component, then you must also create a GroupAttributeDescriptor object, which holds the metadata required for the group information, including the list of attributes that belong to it. You will also need to supply the logic needed by the iterator and dynamic component to display the groups.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To create the model using groups:

  1. Create the attribute definition class, as described in Step 1 of Section 21.2.1, "How to Create the Model Without Groups," but include an attribute definition for category as a String. The category will be used to define the groups.

  2. When you create your attributes, assign values for category for the attributes that need them. For example, you might assign the value "Employee Personal" to the Empname and Empno attributes, and the value "Department Info" to the Deptno and Deptname attributes.

  3. Include category in the BaseAttributeDescriptor class by providing a get method for it. However, the get method should have the signature getGroupName. Example 21-9 shows how the get method might be coded.

    Example 21-9 Get Method for a Category Uses groupName

    public String getGroupName() {
      return _attributeDef.getCategory();
    }
    
  4. Create a class for the GroupAttributeDescriptor object that extends GroupAttributeDescriptor. This object provides a name for the group and a list of the attributes in the group. Example 21-10 shows a GroupAttributeDescriptor class.

    Example 21-10 Class for the GroupAttributeDescriptor Object

    public class TestGroupAttributeDescriptor extends GroupAttributeDescriptor {
      public TestGroupAttributeDescriptor(String groupName, List<BaseAttributeDescriptor> attributeList) {
         _groupName = groupName;
        _flatAttributes = attributeList;
      }
     
      @Override
      public String getName() {
        return _groupName;
      }
     
      public List<? extends Descriptor> getDescriptors() {
        return _flatAttributes;
      }
     
      public String getDescription() {
        return "This is a group";
      }
     
      public String getLabel() {
        return _groupName;
      }
     
      private List<BaseAttributeDescriptor> _flatAttributes;
      private String _groupName;
     
    }
    
  5. In your AttributesModel class, add the hierarchicalAttributes that represent the groups. To do this, you might add logic that examines the BaseAttributeDescriptor object, and if it contains a group name, it adds that to the hierarchicalAttributes object. Example 21-11 shows how you might code that logic.

    Example 21-11 Logic to Add Groups to the hierarchicalAttributes

    for (TestDynamicComponentPageDef.TestAttributeDef demoAttrDef : attributeList) {
      TestAttributeDescriptor attrDesc = new TestAttributeDescriptor(demoAttrDef);
        _flatAttributes.add(attrDesc);
        String groupName = attrDesc.getGroupName();
        if (groupName != null && !groupName.isEmpty()) {
          List<BaseAttributeDescriptor> list = groupMap.get(groupName);
            if (list == null) {
              list = new ArrayList<BaseAttributeDescriptor>();
              groupMap.put(groupName, list);
            }
            list.add(attrDesc);
          } else {
            _hierAttributes.add(attrDesc);
          }
        }
     
      for (String groupName : groupMap.keySet()) {
        TestGroupAttributeDescriptor groupMetadata = 
          new TestGroupAttributeDescriptor(groupName, groupMap.get(groupName));
        _hierAttributes.add(groupMetadata);
      }
    }
    

    For a complete example, see the TestAttributesModel inner class of the TestDynamicComponentBean managed bean in the oracle.adfdemo.view.feature.rich.dynamicFaces package, found in the Application Sources directory of the ADF Faces application.

21.3 Adding a Dynamic Component as a Form to a Page

When you use the dynamic component in a form, you need to include an iterator. The iterator is what access the AttributesModel to get the attributes and their definitions. The dynamic component then gets the information for each instance the iterator stamps out, and determines what component to use, and how to configure it.

If you want to group your attributes in the form, then you also need to use a switcher component with two facets. One facet will display the groups with their associated attributes, while the other facet will display any attributes not associated with a group.

21.3.1 How to Add a Dynamic Component as a Form without Groups to a Page

To use a dynamic component in a form, you use the panelFormLayout component, an iterator, and the dynamic component. If you want to provide a way to navigate between records in the form, then you also need to use buttons.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To add a dynamic component as a form without groups to a page:

  1. Create a panelFormLayout component, as described in Section 9.7, "Arranging Content in Forms."

  2. In the Components window, from the Operations panel, drag and drop an Iterator as a child to the panelFormLayout component.

  3. In the Properties Window, set the following:

    • Value: An EL expression that resolves to the attributes property on the AttributesModel object, for example:

      #{TestDynCompBean.attributesModel.attributes}"
      
    • Var: A String that can be used to access the attributes on the model, for example: attr

  4. From the Components window, drag and drop a Dynamic Component as a child to the iterator component.

  5. In the Properties Window, set the following:

    • AttributeModel: An EL expression that resolves to the variable created for the iterator, for example: #{attr}.

    • Value: An EL expression that resolves to each attribute name on the AttributesModel object, for example:

      #{TestDynCompBean.value[attr.name]}"
      
  6. If you want to provide logic to navigate between the records, add the buttons in a panelGroupLayout component, and use the action event to access the next and previous records. For information about using buttons, see Section 20.3.1, "How to Use Buttons and Links for Navigation and Deliver ActionEvents."

    Example 21-12 shows buttons whose actionListener attributes are bound to logic on a managed bean that navigates between the records in the model.

    Example 21-12 Using Buttons to Navigate Between Records in an AttributesModel

    <af:panelGroupLayout layout="horizontal" id="pgl2">
      <af:button text="Previous" id="cb2" actionListener="#{TestDynCompBean.previous}"
                 disabled="#{!TestDynCompBean.previousEnabled}"/>
      <af:button text="Next" id="cb1" actionListener="#{TestDynCompBean.next}"
                 disabled="#{!TestDynCompBean.nextEnabled}"/>
    </af:panelGroupLayout>
    

    Example 21-13 shows the corresponding managed bean code.

    Example 21-13 Managed Bean Code to Access the Next Record for a Dynamic Component

    public Map[] getValues() {
      return _DATA;
    }
     
    public Map getValue() {
      return _DATA[currentRowIndex];
    }
    
    public void next(ActionEvent actionEvent) {
      if (currentRowIndex < _DATA.length - 1)
          currentRowIndex++;
    }
     
    public boolean getNextEnabled() {
      return (currentRowIndex < (_DATA.length - 1));
    }
    

21.3.2 How to Add a Dynamic Component as a Form with Groups to a Page

When you want to group attributes on form using a dynamic component, you need to place the attributes that are part of a group in one part of the form, and the attributes that don't belong to a group, in another. You use the facets of a switcher component to separate the two.

The switcher component is a child of an iterator component. But instead of being bound to the attributes of the AttributesModel, this iterator is bound to the hierarchicalAttributes of the model, and stores those objects in its variable. Another iterator, in the facet of the switcher, is then bound to the descriptors included in the hierarchicalAttributes object. It is from this iterator that the dynamic component gets its information.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To add a dynamic component as a form with groups to a page:

  1. Create a panelFormLayout component, as described in Section 9.7, "Arranging Content in Forms."

  2. In the Components window, from the Operations panel, drag and drop an Iterator as a child to the panelFormLayout component.

  3. In the Properties window, set the following:

    • Value: An EL expression that resolves to the hierarchicalAttributes property on the AttributesModel object, for example:

      #{TestDynCompBean.attributesModel.hierarchicalAttributes}"
      
    • Var: A String that can be used to access the attributes on the model, for example: attr

  4. Drag and drop a Switcher as a child to the iterator component.

  5. In the Properties window, set the following:

    • FacetName: Enter an EL expression that resolves to the descriptorType property on the values returned by the variable on the parent iterator. For example:

      #{attr.descriptorType}
      
    • DefaultFacet: Name the facet that will hold attributes not returned by #{attr.descriptorType} or that do not match any of the defined facets, for example, ATTRIBUTE.

  6. From the Structure window, right-click the switcher component and choose Insert Inside Switcher > Facet.

  7. In the Insert Facet dialog, name the facet GROUP. This will create the facet that will contain the attributes that belong to a group.

  8. In the Components window, from the Text and Selection panel, drag and drop an Output Text. This component will display the group name.

  9. In the Properties window, bind the Value to the label property of the returned groups, using the parent iterator variable. For example, #{attr.label}.

  10. Drag and drop an Iterator as a child to the outputText component. This iterator will iterate through the attributes in each group, and is what the dynamic component will use to display the components and values.

  11. In the Properties window, set the following:

    • Value: An EL expression that resolves to the Descriptor objects returned by the first iterator, using its variable, for example, #{attr.descriptors}.

    • Var: A String that can be used to access the attributes in the descriptors, for example: nestedAttr

  12. Drag and drop a Dynamic Component as a child to the second iterator.

  13. In the Properties Window, set the following

    • AttributeModel: An EL expression that resolves to the variable created for the second iterator, for example: #{nestedAttr}.

    • Value: An EL expression that resolves to each attribute name returned by the second iterator, for example:

      #{TestDynCompBean.value[nestedAttr.name]}
      
  14. In the Structure window, select the outputText and iterator components, right-click these selections, and choose Surround With. In the Surround With dialog, select Group and click OK.

    In the Properties window, bind Title to the label property of the groups returned by the first iterator, for example, #{attr.label}.

  15. Drag and drop another Facet as a child to the switcher component. Name this facet ATTRIBUTE.

  16. Drag and drop another Dynamic Component as a child to the ATTRIBUTE facet, and set the following:

    • AttributeModel: An EL expression that resolves to the variable created for the main iterator, for example: #{attr}.

    • Value: An EL expression that resolves to each attribute name returned by the main iterator, for example:

      #{TestDynCompBean.value[attr.name]}
      

21.4 Adding a Dynamic Component as a Table to a Page

When you use the dynamic component in a table, you need the table's columns to be wrapped in an iterator. The iterator is what access the AttributesModel to get the attributes and their definitions. The column uses this information to determine its header text. The dynamic component then gets the information for each attribute instance the iterator stamps out, and determines what component to use, and how to configure it, and at the same time, uses the variable on the table to determine the data to display.

If you want to group your attributes in the table, then you also need to use a switcher component with two facets. One facet will display the groups with their associated attributes, while the other facet will display any attributes not associated with a group.

21.4.1 How to Add a Dynamic Component as a Table Without Groups to a Page

To use a dynamic component in a table, you bind the table component to the data. Instead of a column component, the direct child of the table is an iterator bound to the attributes property of the AttributesModel. The column is a child of the iterator. The dynamic component is a child to the column.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To add a dynamic component as a table without groups to a page:

  1. In the Components window, from the Data Views panel, drag and drop a Table, and bind it to the data for your table.

    For more information about tables, see Section 12.3, "Displaying Data in Tables."

  2. In the Structure window, delete the column component and its contents that were created for you. You will manually add a column in a later step.

  3. In the Components window, from the Operations panel, drag and drop an Iterator as a child to the table component.

  4. In the Properties Window, set the following

    • Value: An EL expression that resolves to the attributes property on the AttributesModel object, for example:

      #{TestDynCompBean.attributesModel.attributes}"
      
    • Var: A String that can be used to access the attributes on the model, for example: col

  5. From the Components window, drag and drop a Column as a child to the iterator component. In the Properties window, bind HeaderText to the label property of the attributes returned by the iterator, for example, #{col.label}.

  6. From the Components window, drag and drop a Dynamic Component as a child to the column component.

  7. In the Properties Window, set the following

    • AttributeModel: An EL expression that resolves to the variable created for the iterator, for example: #{col}.

    • Value: An EL expression that resolves to each attribute name on the AttributesModel object. Because this needs to be stamped out for every row, you need to bind this to the value returned by the table component, for example, #{row[col.name]}.

21.4.2 How to Add a Dynamic Component as a Table with Groups to a Page

When you group attributes in a table using a dynamic component, the groups display as parent columns. Figure 21-2 shows the Employee object with certain attributes included in the Employee Personal and Department Info groups.

Figure 21-2 Table with Dynamic Component that Uses Groups

Parent group columns display above

You use a switcher component to separate the attributes that belong to groups from the ones that do not. But instead of being bound to the attributes of the AttributesModel, this main iterator is bound to the hierarchicalAttributes of the model, and stores those objects in its variable. Another iterator, in the facet of the switcher, is then bound to the descriptors included in the hierarchicalAttributes object. It is from this iterator that the dynamic component gets its information and the component can display the groups of attributes.

The hierarchicalAttributes of the AttributeModel defines the root-level attributes of the AttributeModel. If a root level attribute is a normal attribute, meaning the attribute type of ATTRIBUTE, then this attribute does not belong to any group, and the table will render one column for the attribute. If a root level attribute is a grouped attribute, meaning the attribute type is GROUP, then there will be a list of regular attributes that belong to this group. The iterator in the facet will iterate through this group, and each attribute inside that group will be stamped as a column. For example, Employee Personal is a GROUP-type attribute, and it contains 3 regular attributes: Employee Number, Name and Salary, as shown in Figure 21-2.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To add a dynamic component as a table with groups to a page:

  1. In the Components window, from the Data Views panel, drag and drop a Table. In the Create ADF Faces Table dialog, select Bind Data Now. Click Browse for the Table Data Collection to open the Select Table Data Collection dialog. Select the values property on the managed bean you created for the model.

    For more information about tables, see Section 12.3, "Displaying Data in Tables."

  2. In the Structure window, delete the column component and its contents that were created for you. You will manually add a column in a later step.

  3. In the Components window, from the Operations panel, drag and drop an Iterator as a child to the table component.

  4. In the Properties Window, set the following

    • Value: An EL expression that resolves to the hierarchicalAttributes property on the AttributesModel object, for example:

      #{TestDynCompBean.attributesModel.heirarchicalAttributes}"
      
    • Var: A String that can be used to access the attributes on the model, for example: column

  5. From the Components window, drag and drop a Column as a child to the iterator component. In the Properties window, bind HeaderText to the label property of the groups returned by the iterator, for example, #{column.label}.

  6. Drag and drop a Switcher as a child to the column component.

  7. In the Properties window, set the following:

    • FacetName: An EL expression that resolves to the descriptorType property of the values returned by the variable on the parent iterator. For example:

      #{column.descriptorType}
      
    • DefaultFacet: Name the facet that will hold the attributes that don't belong to a group, for example, ATTRIBUTE.

  8. From the Structure window, right-click the switcher component and choose Insert Inside Switcher > Facet.

  9. In the Insert Facet dialog, name the facet GROUP. This will create the facet that will contain the attributes that belong to a group.

  10. Drag and drop an Iterator as a child to the GROUP facet component. This iterator will iterate through the attributes in each group, and is what the dynamic component will use to display the components and values.

  11. In the Properties window, set the following:

    • Value: An EL expression that resolves to the Descriptor objects returned by the first iterator, using its variable, for example, #{column.descriptors}.

    • Var: A String that can be used to access the attributes in the descriptors, for example: nestedCol

  12. Drag and drop a Column as a child to the iterator component. In the Properties window, bind HeaderText to the label property of the attributes returned by the parent iterator, for example, #{nestedCol.label}.

  13. Drag and drop a Dynamic Component as a child to the second iterator.

  14. In the Properties Window, set the following

    • AttributeModel: An EL expression that resolves to the variable created for the second iterator, for example: #{nestedCol}.

    • Value: An EL expression that resolves to each attribute name returned by the second iterator, for example:

      #{row[nestedCol.name]}
      
  15. Drag and drop another Facet as a child to the switcher component. Name this facet ATTRIBUTE.

  16. Drag and drop another Dynamic Component as a child to the ATTRIBUTE facet, and set the following:

    • AttributeModel: An EL expression that resolves to the variable created for the main iterator, for example: #{column}.

    • Value: An EL expression that resolves to each attribute name returned by the main iterator, for example:

      #{row[column.name]}
      

21.5 Using Validation and Conversion with Dynamic Components

You add conversion and validation to dynamic components by adding the needed converter or validator tag, and specifying the attribute to validate or convert. Instead of the enabled attribute, dynamic components use the disabled attribute.

Before You Begin

It may be helpful to have an understanding of how dynamic components determine what they should display. For more information, see Section 21.1, "About Determining Components at Runtime."

To use a validator or converter with a dynamic component:

  1. In the Components window, from the Operations panel, drag and drop the needed converter or validator as a child to the dynamic component.

  2. In the Properties window, in the Other section, click the icon that appears when you hover over the Disabled field and choose Expression Builder.

  3. In the Expression Builder enter an expression that resolves to the attribute and provides the needed pattern. Example 21-14 shows two converters and validators that might be used for the different attributes represented by the dynamic component. The DateTime converter will be run on the Hiredate attribute, the Number converter will be run on the Sal attribute, the Length validator will be run on the Job attribute and the LongRange validator will be run on the Sal attribute.

    Example 21-14 Validation and Conversion for a Dynamic Component

    <af:dynamicComponent value="#{DynCompBean.value[attr.name]}"
                         attributeModel="#{attr}" id="dc1"/>
      <af:convertDateTime disabled="#{attr.name == 'Hiredate' ? false : true}"
                          pattern="yyyy/MM/dd"/>
      <af:convertNumber disabled="#{attr.name == 'Sal' ? false : true}"
                        pattern="#,###,###" />
      <af:validateLength disabled="#{attr.name == 'Job' ? false : true}"
                         maximum="10" hintMaximum="maxmum length is 10"/>
      <af:validateLongRange disabled="#{attr.name == 'Sal' ? false : true}"
                            minimum="1000"/>
    </af:dynamicComponent>
    

21.6 Using Dynamic and Static Components Together

When you create a dynamic form, you essentially create a block of components that are rendered dynamically. It is possible place static components before and after that block of components, but you can not place static components within that block.

Even so, you can create complex forms with a number of different dynamic and static components. And you can have static components interspersed with dynamic components at a fairly granular level.

For example, say you have a form where you need to display name and address information, both in English and in Japanese. For the interest of this example, let's say the Japanese information will be displayed using dynamic components, while the English will be displayed with static components. Figure 21-3 shows how you might organize the fields by having the dynamic Japanese content display after the static content.

Figure 21-3 Dynamic Components Can Be Placed After Static Components

Dynamic Components Can Be Placed After Static Components

Example 21-15 shows how you might create the form using static inputText components and dynamic components that access the attributes in the Name and Address groups.

Example 21-15 Static Components Before Dynamic Components

<panelFormLayout ... >
  <af:group title="Name">
    <af:inputText value="#{myBean.firstName} id="it1" label="First Name"/>
    <af:inputText value="#{myBean.middleName} id="it2" label="Middle Name"/>
    <af:inputText value="#{myBean.lastName} id="it3" label="Last Name"/>
    <af:iterator
             value="#{DynCompBean.attributesModel.hierarchicalAttributes("Name")}"
                 var="attr" id="iter1">
      <af:dynamicComponent id="dc1"
                           value="#{DynCompBean.value[attr.name]}"
                           attributeModel="#{attr}"/>
    </af:iterator>
  </af:group>
  <af:group title="Address">
    <af:inputText value="#{myBean.streetAddress}" id="it4" 
                  label="Street Address"/>
    <af:inputText value="#{myBean.City.inputValue}" id="it5" label="City"/>
    <af:inputText value="#{myBean.State.inputValue}" id="it6" label="State"/>
    <af:iterator
          value="#{DynCompBean.attributesModel.hierarchicalAttributes("Address")}"
          var="attr" id="iter1">
      <af:dynamicComponent id="dc1"
                         value="#{DynCompBean[attr.name].inputValue}"
                         attributeModel="#{attr}"/>
    </af:iterator>
  </af:group>
</panelFormLayout>

Now say that instead of having the Japanese name and address information separate from the English, you want them interspersed, as shown in Figure 21-4.

Figure 21-4 Dynamic Components Can Be Placed With Static Components

Dynamic Components Can Be Placed After Static Components

In this case, you would not use groups in the dynamic component, but instead would access only the needed attribute (and not all attributes), as shown in Example 21-16.

Example 21-16 Dynamic Components With Static Components

<af:group title="Name">
  <af:inputText value="#{myBean.firstName} id="it1" label="First Name"/>
  <af:iterator value="#{DynCompBean.attributesModel.attributes("Japanese First Name")}"
               var="attr" id="iter1">
    <af:dynamicComponent id="dc1" value="#{DynCompBean.value[attr.name]}"
                         attributeModel="#{attr}"/>
  </af:iterator>
  <af:inputText value="#{myBean.middleName} id="it2" label="Middle Name"/>
  <af:inputText value="#{myBean.lastName} id="it3" label="Last Name"/>
  <af:iterator value="#{DynCompBean.attributesModel.attributes("Japanese Last Name")}"
               var="attr" id="iter1">
    <af:dynamicComponent id="dc1" value="#{DynCompBean.value[attr.name]}"
                         attributeModel="#{attr}"/>
  </af:iterator>
</af:group>
<af:group title="Address">
  <af:inputText value="#{myBean.streetAddress}" id="it4" label="Street Address"/>
  <af:iterator value="#{DynCompBean.attributesModel.attributes("Japanese Street Address")}"
               var="attr" id="iter1">
    <af:dynamicComponent id="dc1" value="#{DynCompBean.value[attr.name]}"
                         attributeModel="#{attr}"/>
  </af:iterator>
. . .
</af:group>