Skip Headers
Oracle® Application Development Framework Case Manual
10g Release 2 (10.1.2)  
B19163-01
  Go To Documentation Library
Library
Go To Product List
Product
Go To Table Of Contents
Contents

Previous
Previous
Next
Next
 

9 Lesson Six: Allowing the User to Edit Their Account

This chapter contains the following sections:

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

This image shows the list binding editor for the Country binding in lesson 6.

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:

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

This image shows the list binding editor for the Country binding in lesson 6.

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:

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

This image shows the list binding editor for the Country binding in lesson 6.

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.