Layout Pattern: Compound Component in Form

An Oracle JDeveloper Best Practices Document
March, 2010

Pattern

Layout Pattern: Compound Component in Form

Pattern Revision

1.0

Technologies

ADF Faces

Keywords

Form, Alignment

Forum

Pattern Team

Publish date

March 2010

Contents

Layout Description

This pattern describes the scenario where a single logical slot in a panelFormLayout needs to contain multiple physical components as is shown in this sketch:

Compound Component Layout in Form

In this case notice the following requirements

  1. The label of the compound component aligns with the other labels within the form layout and the first component of the compound component is aligned with the start of all the other components within this column and not indented.
  2. The rightmost edge of the second component within the compound component is aligned with the end of the other fields in the form (this assumes that all of the components within the form have equal definitions for the columns property.

Implementation

To achieve this layout there are several steps:

Step 1: Use a Container to Aggregate the Components

The core to this implementation is the use of a panelLabelAndMessage component which is designed specifically for this purpose of allowing several logical components, or components without their own label property to be correctly laid out in a panelFormLayout.

Step 2: Control the Alignment of Hosted Components

By default, the panelLabelAndMessage will lay out its children vertically. In this case we want the components side-by-side. So simply use a horizontal panelGroupLayout as the immediate child to enclose its content. The child components are then placed into this panelGroupLayout. Use a spacer, or if there are more than two components, the Separator facet of the panelGroupLayout containing a spacer, to control the precise gap between the sub-components as required.

Step 3: Remove Unwanted Labels from Sub-Components

Next you need to make sure that the layout engine does not allocate space for labels on the subcomponents. The label is being defined by the panelLabelAndMessage. To achieve this, set the simple property to “true” on each of the child components. You should also set the for property on the parent panelLabelAndMessage to the relevant child component id. This will endure that error messages are correctly displayed.

Step 4: Ensure Right Alignment of Second Component

If your Form has a ragged right margin then there is no more to do, however, if you want the components to right align as well as left align within the logical form, then there is this additional step.

The approach to doing this is to increase the length of the second child to take up available space and maintain a fixed gap between the child components.

Components such as the selectOneChoice do not stretch and are controlled by the size of their data, therefore you will have to control the size manually using styles. There are two style-related options available on these components, inlineStyle and contentStyle. In the case of the selectOneChoice, setting width in the inlineStyle property will apply to the component as a whole, including its label if it has one.  The contentStyle property is used to control just the width of the drop down box itself and therefore is the property to use. Unfortunately getting the exact width, is a matter of trial and error. If you use pixels as the measurement unit, bear in mind that this is probably being used to align with input fields which are sized according to their columns property. This being the case, changes to the skin or browser zoom level may disrupt this relationship. For precise alignment that can survive zooming by the user, you may have to set explicit sizes in em’s for all the components in the form, or use the fieldWidth and LabelWidth properties of the panelFormLayout.

The code for this component will look like this:

<af:selectOneChoice id="soc1"
                simple="true"
                value="#{bindings.Currency.inputValue}"
                contentStyle="width:11em;">

  <f:selectItems id="si1" value="#{bindings.Currency.items}" />

</af:selectOneChoice>

Logical View

The following diagram summarizes the way in which the containers are arranged:

Logical View

The Result

The following screenshot shows the implemented result:

Result

 

Appendix - Complete Code

Complete Code

<af:panelFormLayout id="pfl1" labelAlignment="start">

  <af:inputText id="it1"
            value="#{bindings.Id.inputValue}"
            label="#{bindings.Id.hints.label}"/>

  <af:inputText id="it2"
            value="#{bindings.Description.inputValue}"
            label="#{bindings.Description.hints.label}/>"

  <af:panelLabelAndMessage id="plam1"
                       label="Price" >

    <af:panelGroupLayout id="pgl1"
                     layout="horizontal"
                     halign="start"
                     valign="top">

      <af:inputNumberSpinbox id="ins1"
                         simple="true"
                         value="#{bindings.Price.inputValue}"
                         columns="4"/>

      <af:selectOneChoice id="soc1"
                      simple="true"
                      value="#{bindings.Currency.inputValue}"
                      contentStyle="width:11em;">

         <f:selectItems id="si1"
                    value="#{bindings.Currency.items}"/>

      </af:selectOneChoice>

    </af:panelGroupLayout>

  </af:panelLabelAndMessage>

  <af:commandButton text="Save" id="cb1"/>

</af:panelFormLayout>