Skip Headers

Oracle® Application Development Framework Case Manual 10g Release 2 (10.1.2) B19163-01 |
| Library | Product | Contents

Previous | Next |

# 9 Lesson Six: Allowing the User to Edit Their Account

This chapter contains the following sections:

- Section 9.1, "Introduction"

- Section 9.2, "Setting Up the Model Layer Data"

- Section 9.3, "Laying Out the Edit Account Page"

- Section 9.4, "Analyzing the Binding Container for the Edit Account Page"

- Section 9.5, "Showing Read-Only Data in a Form"

- Section 9.6, "Creating Input Fields in the Form"

- Section 9.7, "Using EL to Work with Labels, Tooltips, and Other Metadata"

- Section 9.8, "Including a Databound Poplist Control"

- Section 9.9, "Alternative to a Databound Poplist Using Custom Renderer"

- Section 9.10, "Hands-On for Lesson 6"

## 9.1 Introduction

In the previous lesson, we created the account object for a new user by generating a data entry form, using a generic, metadata-driven approach. In Lesson 6, we examine another way to display a data entry form, this time to edit user account information: the `editexistingaccount.jsp` page uses the traditional JSP page layout approach of placing each control inside an HTML form.

## 9.2 Setting Up the Model Layer Data

The `/EditAccountAction` data action sets up the model layer in its `initializeModelForPage()` method. It calls the custom service method `prepareToEditAccountInfoFor()` on the `ToyStoreService` interface, passing in the name of the current user as an argument. The implementation of this method in the `ToyStoreServiceImpl` class looks like this:

    /* From: toystore.model.services.ToyStoreServiceImpl */
       public boolean prepareToEditAccountInfoFor(String username) {
         Key k = new Key(new Object[] { username });
         ViewObject vo = getAccounts();
         /*
          * We don't want the view object to execute any other query
          * than the one row we will be finding by key, so we mark
          * its max fetch size to zero.
          */
         vo.setMaxFetchSize(0);
         Row[] r = vo.findByKey(k, 1);
         if (r.length < 1) {
           return false;
         }
         Row rowFound = r[0];
         /*
          * Set the row we found as the current row in the VO
          */
         vo.setCurrentRow(rowFound);
         return true;
       }

The above service method performs the following three basic steps:

1. It creates an `oracle.jbo.Key` object based on the current user's name passed in.

2. It looks up an existing row in the `Accounts` view object by passing this key to the `findByKey()` method on the view object.

3. It sets that row as the current row in the view object.

## 9.3 Laying Out the Edit Account Page

In the corresponding JSP page, named `editexistingaccount.jsp`, we use the `<html:form>` tag from the Struts HTML tag library to implement the postback pattern by having its action post back to the data page like this:

    <html:form action="editaccount.do" method="post">

At runtime, the Struts `<html:form>` tag sees the action attribute value of `/updateaccount.do` and uses it, along with its action mapping information, to determine that the form bean named `DataForm` is the one that should be used to render this form. The `DataForm` form bean is defined in `struts-config.xml` to use our Oracle ADF `BindingContainerActionForm` class.

Since we're rendering the data entry form for just a single "row" of user account information, we don't need to use the JSTL `<c:forEach>` tag in this page and we don't need a range binding in our binding container. We simply format the individual fields in the form, using normal HTML table tags to get the prompts and controls to line up nicely. As this page shows off several different techniques in use, the following section will highlight each of the important ones.

## 9.4 Analyzing the Binding Container for the Edit Account Page

Figure 9-1, "The UI Model Tab Shows the Binding Container for the Edit Account Page", shows the binding container for the edit account page. Notice that we have basic attribute bindings for all of the `Accounts` object attributes except for `Country`, which is a list binding (its icon represents a poplist). We have two iterator bindings: `AccountsIterator` for the main account information we're editing, and `CountryListIterator` to supply a poplist with the valid country names from which the user can choose for the `Country` attribute.

Figure 9-1 The UI Model Tab Shows the Binding Container for the Edit Account Page

We also have an action binding named `save` that is bound to the built-in `Commit` operation on the `ToyStoreService` data control.

## 9.5 Showing Read-Only Data in a Form

The following sample shows the tags used by the page to output the HTML table row containing the prompt and data for the username field.

    <%-- Username field --%>
    <tr>
       <th align="right" title="<bean:message key="account.username.tooltip"/>">
         <bean:message key="account.username.label"/>
       </th>
       <td title="<bean:message key="account.username.tooltip"/>">
         <c:out value="${bindings.Username}"/>
       </td>
    </tr>

Since `username` in this application is not updateable once it has been created, we don't need to render an HTML form control for the data. Using the `<c:out>` tag, we just output the value of the field for display using its corresponding binding object. The `<bean:message>` tags handle outputting translatable strings from the default `ToyStoreResources.properties` file to display the tooltip and label for the username.

## 9.6 Creating Input Fields in the Form

When data needs to be entered or edited, you can use a number of other tags in the Struts HTML library to render databound controls. The following code uses the `<html:password>` tag to show the `Password` property:

    <%-- Password field --%>
    <tr>
       <th align="right" title="<bean:message key="account.password.tooltip"/>">
         <bean:message key="dataentryform.mandatory"/>
         <bean:message key="account.password.label"/>
       </th>
       <td title="<bean:message key="account.password.tooltip"/>">
         <html:password property="Password" size="25" maxlength="30"/>
       </td>
       <td><html:errors property="Password"/></td>
    </tr>

Recall that the Oracle ADF `BindingContainerActionForm` presents Struts (in this case, tags from the Struts HTML tag library) with a `DynaActionForm` bean having properties that are named for, and wired to, the bindings in your current binding container. So, when the `<html:password>` tag gets and sets the value of the `Password` property on this form bean, behind the scenes Oracle ADF is coordinating the properties of that form bean with the corresponding binding objects.

The above sample also illustrates using the Struts HTML tag `<html:errors>` to display any validation errors that are specific to the `Password` attribute. Of course, when the form is first rendered there won't be any validation errors, so this table cell will be empty. However, if the user submits the form and the model layer throws validation errors in, when this page is rendered again any errors related to the password will show up next to the password field on the screen.

Also, since the password field is mandatory, we've included a `<bean:write>` tag to show the string corresponding to the key `dataentryform.mandatory` as a visual marker for the user that the field is required. By default, we render an asterisk.

## 9.7 Using EL to Work with Labels, Tooltips, and Other Metadata

Oracle ADF entity object and view object components have a number of built-in features that allow developers to define control hints like locale-sensitive labels, tooltips, and format masks. The Oracle ADF binding layer exposes this metadata directly on the binding objects for convenient access by your view layer pages. The `<c:out>` tags shown below illustrate the EL expressions for the tooltip and label information that have been associated with the business object attributes or the view object attributes. If an entity object has defined a tooltip for one of its attributes named `Firstname`, for example, then this tooltip is inherited by any view objects that include `Firstname`. Of course, the view object can also override these control hints if necessary.

    <%-- Firstname field --%>
    <tr>
       <th align="right" title="<c:out value='${bindings.Firstname.tooltip}'/>">
         <c:if test="${bindings.Firstname.mandatory}">
           <bean:message key="dataentryform.mandatory"/>
         </c:if>
         <c:out value="${bindings.Firstname.label}"/>
       </th>
       <td>
         <html:text property="Firstname" size="30" maxlength="35"/>
       </td>
       <td><html:errors property="Firstname"/></td>
    </tr>

Each binding object exposes runtime metadata about the objects to which it is bound that you can access at runtime using EL expressions. For example, the control value binding for an attribute exposes information about the underlying attribute in the model layer. The above sample shows an example of using this metadata to detect at runtime whether a given attribute, like `Firstname`, is mandatory or not. We can use this information, combined with the JSTL `<c:if>` tag, to conditionally output the mandatory marker on a required field.

    <c:if test="${bindings.Firstname.mandatory}">
       <bean:message key="dataentryform.mandatory"/>
    </c:if>

JDeveloper Tip: To get a quick review of all the available properties on a binding object, just click on the binding in the UI Model tab of the Structure window and press the F1 key. The online help topic for the appropriate binding object appears in an IDE window for your reference.

## 9.8 Including a Databound Poplist Control

Finally, we look at an example of a more sophisticated databound form control, such as a poplist showing the country of residence for a user. There are two dimensions to the poplist control:

- The value of the underlying `Country` binding, reflected by the selection in the list

- The list of all available country names to chose from

Figure 9-2, "The Poplist Displays the Current Value and Valid Values List", illustrates the poplist.

Figure 9-2 The Poplist Displays the Current Value and Valid Values List

Oracle ADF provides more sophisticated binding objects to handle controls like this one, which have multiple facets to their data binding requirements. The Oracle ADF list binding caters specifically to poplist-type controls that need to manage both a current bound attribute value and a list of valid choices to present to the user. Oracle ADF also supplies a tree binding object that is useful for displaying hierarchical data.

When you click the UI Model tab of the Structure window with editexistingaccount.jsp selected in the Application Navigator, you'll see the bindings described above. Now, select CountryListIterator and open the Property Inspector: you'll see that the RangeSize property has a value of `-1`. This value indicates that you want the range of the iterator to include all rows in the list of countries, rather than only a partial set.

Best Practice Tip: The iterator binding range size defaults to 10. For iterators driving the list of choices in a list binding, you will nearly always want to set the range size to be -1 as we've done here.

Right-click the Country binding and choose Edit from the context menu. You will see the List Binding Editor shown in the screenshot below. The editor allows you to see the binding metadata required to support the Country poplist:

- `CountryListIterator`, the datasource for the list of available choices.

- `AccountsIterator`, the iterator whose current row will be used both to determine the current value of the binding and to update the binding when the user selects a new item from the list``.

- `Code` and `Country`, the source LOV and target attribute pairs, show that the value of the `Code` property from the selected row in `CountryList` will be set on the `Country` property on the current row of the target `AccountsIterator`.

If you click the LOV Display Attributes tab, you can observe that the `Description` attribute from the `CountryListIterator` is indicated as the value to be displayed to the user in the list. Figure 9-3, "The List Binding Editor for the Country List Binding", shows the editor.

Figure 9-3 The List Binding Editor for the Country List Binding

The example below shows how to use `<html:select>` and `<html:optionsCollection>` to leverage the `Country` list binding and display the poplist in our page. The `<html:select>` element is bound to the `Country` property of the form bean, which corresponds to our list binding object. The `<html:optionsCollection>` element gets its data from the nested, list-valued `displayData` property of that same `Country` binding. The beans in this display data collection each have a `prompt` and an `index` property, so in the following code we use those as the label and value (respectively) for each option in the list:

    <%-- Country field --%>
    <tr>
       <th align="right" title="<c:out value='${bindings.Country.tooltip}'/>">
         <c:if test="${bindings.Country.mandatory}">
           <bean:message key="dataentryform.mandatory"/>
         </c:if>
         <c:out value="${bindings.Country.label}"/>
       </th>
       <td>
         <html:select  property="Country" >
           <html:optionsCollection label="prompt"
           value="index"
           property="Country.displayData" />
         </html:select>
       </td>
       <td><html:errors property="Country"/></td>
    </tr>

Note: For bandwidth optimization, the Oracle ADF binding layer expects the nonvisible values of a list binding to be the zero-based `index` number in their `displayData` collection. The Oracle ADF list binding handles translating the underlying Country value (like `IT`, for example) into an index position (like `86`) in the list of values on both read and write of the binding value.

## 9.9 Alternative to a Databound Poplist Using Custom Renderer

In contrast to the above approach, which relied on the control to render the poplist in the form, we can accomplish the same thing in a more generic, metadata-driven way by performing these steps:

1. Create a custom renderer class to determine how to handle the country list.

2. Set a custom property on the attribute of the Oracle ADF Business Components to specify the custom renderer.

3. Work with the `<adf:inputrender>` tag in the JSP page to apply the custom renderer.

Note: The `<adf:inputrender>` tag implementation consults the value of the `EditRenderer` attribute property to determine whether the attribute has specified a custom renderer. If none is specified, Oracle ADF picks an appropriate control to render the data.

This use of the custom renderer is demonstrated by the `formControl.jsp` page introduced in Lesson 5, and described in more detail below.

In the case of the `formControl.jsp` page, we've specified the class name `toystore.fwk.view.ListBindingPoplistRenderer`, which implements a customized poplist renderer for Oracle ADF list bindings. This custom field renderer extends the default `oracle.jdeveloper.html.StaticPickList` renderer to populate some of its properties based on information it can retrieve from the list binding object. The source code for the custom renderer (from the `FwkExtensions` project) is shown below:

    package toystore.fwk.view;
    import java.util.List;
    import java.util.Map;
    import oracle.jbo.Row;
    import oracle.jbo.html.BindingContainerDataSource;
    import oracle.jbo.uicli.binding.JUControlBinding;
    import oracle.jbo.uicli.binding.JUCtrlListBinding;
    import oracle.jdeveloper.html.StaticPickList;
    /**
      * Extends the oracle.jdeveloper.html.StaticPickList renderer to drive
      * off of a list binding.
      */
    public class ListBindingPoplistRenderer extends StaticPickList {
       /**
        * Overrides renderToString() in StaticPickList
        */
       public String renderToString(Row row) {
         BindingContainerDataSource ds = (BindingContainerDataSource)getDatasource();
         JUControlBinding b = ds.getControlBinding();
         String[] labels = null;
         String[] values = null;
         if (b instanceof JUCtrlListBinding) {
           JUCtrlListBinding listBinding = (JUCtrlListBinding)b;
           List valueList = listBinding.getDisplayData();
           int size = valueList.size();
           values = new String[size];
           labels = new String[size];
           for (int z = 0; z < size; z++) {
             labels[z] = (String)((Map)valueList.get(z)).get("prompt");
             values[z] = Integer.toString(z);
           }
           setValue(Integer.toString(listBinding.getSelectedIndex()));
         }
         setDataSource(labels,values);
         return super.renderToString(row);
       }
    }

You can see that the code accesses the `JUControlBinding` object from the datasource, that it checks to be sure the object is a `JUCtrlListBinding` object, and that it calls the `getDisplayData()` method on the list binding to access the list display data. In order to populate the `String[]` variables for the labels and the values, it iterates over the display data collection and adds the `prompt` attribute from each bean in the collection to the `label` array. Since the Oracle ADF binding layer will expect the value returned from the page to be the numerical row number, we populate the `values` array by converting the loop variable `z` to a string on each iteration. The net effect is that when our generic `formControl.jsp` "component" page renders an HTML form for the bindings in the current binding container, the `Country` binding will be rendered as a databound poplist populated from the display data collection named `CountryList`.

If you adopt a generic data form rendering technique like the one employed by the `formControl.jsp` page in your applications, you can more easily ensure that all data entry forms in your application look and act similarly.

## 9.10 Hands-On for Lesson 6

The following hands-on shows how changing the iterator `rangesize` property controls the display range.

1. In the Application Navigator, locate editexistingaccount.jsp in the WEB-INF/jsp folder and select it so that it appears highlighted.

2. Open the Structure window and select the UI Model tab to view the list of binding definitions in the WEB_INF_jsp_editexistingaccountUIModel definition file.

3. In the Structure window, double-click CountryListIterator and observe that CountryListIterator is bound to the CountryList data collection. Click Cancel to exit the editor.

4. With CountryListIterator selected, open the Property Inspector and locate the RangeSize property with the value `-1`.

5. In the Property Inspector, change the RangeSize property value to `10` and press Enter. This will limit the display list to just ten objects from the bound data collection.

6. Right-click the home.do action and choose Run to launch the application. Click the Login icon and enter the `J2EE`/`J2EE` username and password. Click the Edit Account icon and display the dropdown list for the Country field. The list should be limited to just ten rows.

The iterator binding is a runtime object that your application creates to access the Oracle ADF binding context. The iterator binding holds references to the bound data collection, it accesses the collection, and it iterates over its data objects. You can set the number of data objects to be fetched from the bound data collection, such that only the number is displayed on the page. The range you specify defines a window you can use to access a subset of the data objects in the collection. By default, the range size is set to just ten data objects.