Oracle® Application Development Framework Developer's Guide 10g Release 3 (10.1.3.0) Part Number B28967-02 |
|
|
View PDF |
This chapter describes how to use the Data Control Palette to create databound forms using ADF Faces components.
This chapter includes the following sections:
You can create UI widgets that allow you to display and collect information using data controls created for your business services. For example, using the Data Control Palette, you can drag an attribute for an item, and then choose to display the value as either read-only text or as an input text field with a label.
Instead of having to drop individual attributes, JDeveloper allows you to drop all attributes for an object at once as a form. The actual UI components that make up the form depend on the type of form dropped.
Once you drop the UI components, you can then drop built-in operations as command UI components that allow users to operate on the data. For example, you can create buttons that allow users to navigate between data objects displayed in the form. You can also modify the default components to suit your needs.
This chapter explains the following:
How to create individual databound text fields
How to create a form consisting of multiple text fields
How to add operations that allow you to navigate between the data objects displayed in a form
How to modify the form once it has been created
To create text fields, you bind ADF Faces text UI components to attributes on a data control using an attribute binding. JDeveloper allows you to do this declaratively without the need to write any code. Additionally, JDeveloper provides a complete WYSIWYG development environment for your JSF pages, meaning you can design most aspects of your pages without needing to look at the code.
To create a text field that can display or update an attribute, you must bind the UI component to the attribute using a data control. JDeveloper allows you to do this declaratively by dragging and dropping an attribute of a collection from the Data Control Palette.
To create a bound text field:
From the Data Control Palette, select an attribute for a collection (for a description of which icon represents attributes and other objects in the Data Control Palette, see Section 5.2.1, "How to Understand the Items on the Data Control Palette").
For example, Figure 6-1 shows the problemDescription
attribute under the ServiceRequest
collection for the findAllServiceRequest()
method of the SRPublicFacade
data control in the SRDemo application. This is the attribute to drop to display the problem description for a service request.
If you wish to create input text fields used to collect data, you can use either a custom method or one of the data control's built-in creation methods. For procedures, see Section 10.7, "Creating an Input Form for a New Record".
Drag the attribute onto the page, and from the context menu choose the type of widget to display or collect the attribute value. For an attribute, you are given the following choices:
Texts
ADF Output Text with a Label: Creates a panelLabelAndMessage
component that holds an ADF Faces outputText
component. The label
attribute on the panelLabelAndMessage component is populated.
ADF Output Text: Creates an ADF Faces outputText
component. No label is created.
ADF Input Text with a Label: Creates an ADF Faces inputText
component with a validator
. The label
attribute is populated.
Tip:
For more information about validators, see Chapter 12, "Using Validation and Conversion".ADF Input Text: Creates an ADF Faces inputText
component with a validator
. The label
attribute is not populated.
ADF Label: An ADF Faces outputLabel
component.
Single selections
These widgets display lists. For the purposes of this chapter, only the text widgets will be discussed. To learn about lists and their bindings, see Section 11.7, "Creating Databound Dropdown Lists".
When you drag an attribute onto a JSF page and drop it as a UI component, among other things, a page definition file is created for the page (if one does not already exist), using the name of the JSF page including the page's package name, and appending PageDef
as the name of the page definition file. For example, the page definition file for the SREdit page is app_staff_SREditPageDef.xml
. For a complete account of what happens when you drag an attribute onto a page, see Section 5.2.3, "What Happens When You Use the Data Control Palette". Bindings for the iterators and methods are created and added to the page definition file if they do not already exist, as are the bindings for each attribute. Additionally, the necessary JSPX page code for the UI component is added to the JSF page.
Whenever you create UI components on a page by dropping an item that is part of a collection from the Data Control Palette (or you drop the whole collection as a form or table), JDeveloper creates a method iterator binding if it does not already exist. A method iterator binding references an iterator for the data collection, which facilitates iterating over its data objects. It also manages currency and state for the data objects in the collection. An iterator binding does not actually access the data. Instead, it simply exposes the object that can access the data, and it specifies the current data object in the collection. Other bindings then refer to the iterator binding in order to return data for the current object or to perform an action on the object's data.
Tip:
There is one iterator created for each collection. This means that when you drop two attributes from the same collection (or drop the collection twice), they use the same iterator. This is fine, unless you need the iterator to behave differently for the different components. In that case, you will need to manually create separate iterators. For procedures and an example, see Section 10.9, "Conditionally Displaying the Results Table on a Search Page".For example, if you drop the problemDescription
attribute under the ServiceRequest
collection for the findAllServiceRequest()
method, JDeveloper creates a method iterator binding for the returned ServiceRequest
collection.
The iterator binding's rangeSize
attribute determines how many records will be available for the page each time the iterator binding is accessed. This attribute gives you a relative set of 1-N rows positioned at some absolute starting location in the overall rowset. By default, it is set to 10
. For more information about using this attribute, see Section 6.4.2.2, "Iterator RangeSize Attribute". Example 6-1 shows the method iterator binding created when you drop an attribute from the ServiceRequest
collection for the findAllServiceRequest()
method.
Example 6-1 Page Definition Code for a Method Iterator Binding When You Drop an Attribute from a Method Return Collection
<executables> <methodIterator id="findAllServiceRequestIter" Binds="findAllServiceRequest.result" DataControl="SRPublicFacade" RangeSize="10" BeanClass="oracle.srdemo.model.entities.ServiceRequest"/> </executables>
For information regarding the iterator binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".
JDeveloper also creates an action binding for the findAllServiceRequest
method used to return the collection. Note that this action binding is created as a binding element and not an executable element. Example 6-2 shows the action binding created when you drop an attribute of the ServiceRequest
collection for the findAllServiceRequest()
method.
Example 6-2 Page Definition code for an Action Binding Used by the Iterator
<bindings> <methodAction id="findAllServiceRequest" InstanceName=""SRPublicFacade.dataProvider" DataControl="SRPublicFacade" MethodName="findAllServiceRequest" RequiresUpdateModel="true" Action="999" ReturnName="SRPublicFacade.methodResults.SRPublicFacade_ dataProvider_findAllServiceRequest_result"/>
This metadata allows the ADF binding container to access the attribute by allowing the iterator to access the result property on the associated method binding. Because the iterator is an executable, it is created when the page is loaded, and thus causes the method referenced in the method binding to execute.
In Example 6-1, the iterator is bound to the result property of the findAllServiceRequest
method binding. This means that the iterator will manage all the returned service requests, including determining the current service request or range of service requests.
For information regarding the action binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".
When you drop an attribute from the Data Control Palette, JDeveloper creates an attribute binding that is used to bind the UI component to the attribute's value. This type of binding presents the value of an attribute for a single object in the current row in the collection. Value bindings can be used to both display and collect attribute values.
For example, if you drop the problemDescription
attribute under the ServiceRequest
collection for the findAllServiceRequest()
method as an ADF Output Text w/Label widget onto the SREdit page, JDeveloper creates an attribute binding for the problemDescription
attribute. Note that the attribute value references the findAllServiceRequest
Iter
iterator. This allows the binding to access the attribute value of the current record. Example 6-3 shows the attribute binding for problemDescription
created when you drop the attribute from the ServiceRequest
collection for the findAllServiceRequest()
method.
Example 6-3 Page Definition Code for an Attribute Binding
<bindings> ... <attributeValues id="ServiceRequestproblemDescription" IterBinding="findAllServiceRequestIter"> <AttrNames> <Item Value="problemDescription"/> </AttrNames> </attributeValues> </bindings>
For information regarding the attribute binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".
When you create a text field by dropping an attribute from the Data Control Palette, JDeveloper creates the UI component associated with the widget dropped by writing the corresponding code to the JSF page.
For example, when you drop the ProblemDescription
attribute as an Output Text w/Label widget, JDeveloper creates an EL expression that binds the panelLabelAndMessage
label's
attribute to the label
property of the ProblemDescription
binding. It creates another expression that binds the panelLabelAndMessage
value attribute to the inputValue
property of the ProblemDescription
binding, which in turn is the value of the ProblemDescription
attribute. For more information about binding object properties, see Section A.2.2, "Oracle ADF Data Binding Files".
Example 6-4 shows the code generated on the JSF page when you drop an attribute as an Output Text w/Label widget.
Example 6-4 JSF Page Code for an Attribute Dropped as an Output Text w/Label
<af:panelLabelAndMessage label="#{bindings.ServiceRequestproblemDescription.label}"> <af:outputText value="#{bindings.problemDescription.inputValue}"/> </af:panelLabelAndMessage>
If instead you drop the problemDescription
attribute as an Input Text w/Label widget, JDeveloper creates an inputText
component. As Example 6-5 shows, similar to the output text component, the value is bound to the inputValue
property of the problemDescription
binding. Additionally, the following properties are also set:
label
: bound to the binding's label
property.
required
: bound to the binding's mandatory
property. See Section 12.3, "Adding Validation" for more information about this property.
columns
: bound to the displayWidth
property. This determines how wide the text box will be.
Example 6-5 JSF Page Code for an Attribute Dropped as an Input Text w/Label
<af:inputText value="#{bindings.problemDescription.inputValue}" label="#{bindings.problemDescription.label}" required="#{bindings.problemDescription.mandatory}" columns="#{bindings.problemDescription.displayWidth}"> <af:validator binding="#{bindings.problemDescription.validator}"/> </af:inputText>
For more information about the properties, see Appendix B, "Reference ADF Binding Properties".
When a page is submitted and a new page requested, the application invokes both the JSF lifecycle and the ADF lifecycle. The JSF lifecycle handles the components at the view layer, while the ADF lifecycle handles the data at the model layer.
Specifically, the JSF lifecycle handles the submission of values on the page, validation for components, navigation, and displaying the components on the resulting page and saving and restoring state. The JSF lifecycle phases use a UI component tree to manage the display of the faces components. This tree is a runtime representation of a JSF page: each UI component tag in a page corresponds to a UI Component instance in the tree. The FacesServlet
object manages the request processing lifecycle in JSF applications. FacesServlet
creates an object called FacesContext
, which contains the information necessary for request processing, and invokes an object that executes the lifecycle.
The ADF lifecycle handles preparing and updating the data model, validating the data at the model layer, and executing methods on the business layer. The ADF lifecycle uses the binding container to make data available for easy referencing by the page during the current page request.
The lifecycles are divided into phases. For the two lifecycles to work together, the ADF lifecycle phases are integrated with the JSF lifecycle phases using the JSF event listener mechanism. The ADF lifecycle listens for phase events using the ADF phase listener, which allows the appropriate ADF phases to be executed before or after the appropriate JSF phases.
When an ADF Faces component bound to an ADF data control is inserted into a JSF page for the first time, JDeveloper adds the ADF PhaseListener to faces-config.xml
. Example 6-6 shows the ADF PhaseListener configuration in faces-config.xml
.
Example 6-6 Registering the ADF PhaseListener in faces-config.xml
<faces-config xmlns="http://java.sun.com/JSF/Configuration"> ... <lifecycle> <phase-listener> oracle.adf.controller.faces.lifecycle.ADFPhaseListener </phase-listener> </lifecycle> ... </faces-config>
Figure 6-2 shows how the JSF and ADF phases integrate in the lifecycle.
In a JSF application that uses the ADF Model layer, the lifecycle is as follows:
Restore View: The URL for the requested page is passed to the bindingContext
, which finds the page definition that matches the URL. The component tree of the requested page is newly built or restored. All the component tags, event handlers, converters, and validators on the submitted page have access to the FacesContext
instance. If it's a new empty tree (that is, there is no data from the submitted page), the lifecycle proceeds directly to the Render Response phase. Otherwise, the Restore View phase issues an event which the Initialize Context phase of the ADF Model layer lifecycle listens for and then executes.
For example, for a page that contains an inputText
UI component bound to the ProblemDescription
attribute of a ServiceRequest
returned collection, when the URL is passed in, the page definition is exposed. The UI component is then built. If data is to be displayed, the Initialize Context phase executes. Otherwise, the lifecycle jumps to the Render Response phase.
Initialize Context: The page definition file is used to create the bindingContainer
object, which is the runtime representation of the page definition for the requested page. The LifecycleContext
class used to persist information throughout the ADF lifecycle phases is instantiated and initialized.
Prepare Model: The binding container is refreshed, which sets any page parameters contained in the page definition. Any entries in the executables section of the page definition are refreshed, depending on the value of the Refresh
and RefreshCondition
attributes.
The Refresh
and RefreshCondition
attributes are used to determine when and whether to invoke an executable. For example, there maybe an executable that should only be invoked under certain conditions. Refresh determines the phase in which to invoke the executable, while the refresh condition determines whether the condition has been met. Set the Refresh
attribute to prepareModel
when your bindings are dependent on the outcome from the operation.
If Refresh
is set to prepareModel
, or if no value is supplied (meaning it uses the default, ifneeded
), then the RefreshCondition
attribute value is evaluated. If no RefreshCondition
value exists, the executable is invoked. If a value for RefreshCondition
exists, then that value is evaluated, and if the return value of the evaluation is true, then the executable is invoked. If the value evaluates to false, the executable is not invoked. The default value always enforces execution. If the incoming request contains no POST data or query parameters, then the lifecycle forwards to the Render Response phase.
For more information, see . For details about the refresh attribute, see Section A.7.1, "PageDef.xml Syntax".
In the problem description example, the bindingContainer
invokes the findAllServiceRequestIter
method iterator, which in turn invokes the findAllServiceRequest
method that returns the ServiceRequest
collection. The iterator then iterates over the data and makes the data for the first found record available to the UI component by placing it in the binding container. Because there is a binding for the problemDescription
attribute in the page definition that can access the value from the iterator (see Example 6-3), and since the UI component is bound to the problemDescription
binding using an EL expression (#{bindings.problemDescription.inputValue}
), that data is displayed by that component.
Apply Request Values: Each component in the tree extracts new values from the request parameters (using its decode method) and stores it locally. Most associated events are queued for later processing. If a component has its immediate
attribute set to true
, then the validation, conversion, and events associated with the component are processed during this phase. For more information about validation and conversion, see Chapter 12, "Using Validation and Conversion".
For example, if a user enters a new value into the inputText
component, that value is stored locally using the setSubmittedValue
method on the inputText
component.
Process Validations: Local values of components are converted and validated. If there are errors, the lifecycle jumps to the Render Response phase. At the end of this phase, new component values are set, any validation or conversion error messages and events are queued on FacesContext
, and any value change events are delivered.
For a detailed explanation of this and the next two phases of the lifecycle, see Chapter 12, "Using Validation and Conversion".
Update Model Values: The component's validated local values are moved to the model and the local copies are discarded.
Validate Model Updates: The updated model is now validated against any validation routines set on the model.
Invoke Application: Any action bindings for command components or events are invoked. For a detailed explanation of this and the next two phases of the lifecycle, see Section 9.4, "Using Dynamic Navigation". For a description of action bindings used to invoke business logic, see Section 6.4, "Incorporating Range Navigation into Forms".
Metadata Commit: Changes to runtime metadata are committed. This phase is not used in this release, but will be used in future releases.
Initialize Context (only if navigation occurred in the Invoke Application lifecycle): The page definition for the next page is initialized.
Prepare Model (only if navigation occurred in the Invoke Application lifecycle): Any page parameters contained in the next page's definition are set. Any entries in the executables section of the page definition are used to invoke the corresponding methods in the order they appear.
Prepare Render: The binding container is refreshed to allow for any changes that may have occurred in the Apply Request Values or Validation phases. The prepareRender
event is sent to all registered listeners.
Note:
Instead of displayingprepareRender
as a valid phase for a selection, JDeveloper displays renderModel
, which represents the refresh(RENDER_MODEL)
method called on the binding container.You should set the Refresh
attribute of an executable to renderModel
when the refreshCondition
is dependent on the model. For example, if you want to use the #{adfFacesContext.postback}
expression in a RefreshCondition
of an executable, you must set the Refresh
property to either renderModel
or renderModelIfNeeded
, which will cause the method to be executed during the prepareRender
phase. For more information, see .
Render Response: The components in the tree are rendered as the J2EE web container traverses the tags in the page. State information is saved for subsequent requests and the Restore View phase.
Instead of dropping individual attributes to create a form, JDeveloper allows you to drop a complete form that displays or collects data for all the attributes on an object. For example, the SREdit page was created by dropping the return from the findServiceRequestById
method, which contains all the attributes necessary to edit a given service request.
This section provides information on creating a form that returns data to be viewed or edited. You can also use a constructor or the method itself to create a form used to populate data instead of return data. For more information about creating that type of form, see Section 10.7, "Creating an Input Form for a New Record".
To create a form using a data control, you bind the UI components to the attributes on the corresponding object in the data control. JDeveloper allows you to do this declaratively by dragging and dropping a collection or a structured attribute from the Data Control Palette.
These procedures are for creating a form that displays all objects from a collection returned by a method that takes no parameters. If you want to use a collection returned from a method that takes parameters, you need to have those parameters set in order for the form to display the proper records. For procedures and information about setting parameters, see Section 10.6.1, "How to Create a Form or Table Using a Method That Takes Parameters".
To create a form that allows a user to create a new record, you need to use a method that creates the new instance, given some values for that instance. If your data control was configured to support updates, then it will include constructors, which are objects you can use to create a form that creates a new object, populating values for all attributes on the object. For more information, see Section 10.7, "Creating an Input Form for a New Record".
Whether you use a collection, a constructor, or a method to create a form, you may also want to add a command button that invokes a method to, for example, insert the data into the data source or update the data. For procedures and more information, see Section 10.3, "Creating Command Components to Execute Methods".
To create a basic form:
From the Data Control Palette, select a collection that is a return of a findAll
method.
To display the value of existing attributes, drop the returned collection from a method used to find records. Figure 6-3 shows the ServiceRequest
collection for the findAllServiceRequest()
method from the SRDemo application. This method creates a form with data already populated in the input text fields.
Drag the collection onto the page, and from the context menu choose the type of form to display or collect data for the object. For a form, you are given the following choices:
ADF Form: Launches the Edit Form Fields dialog that allows you to select individual attributes instead of creating a field for every attribute by default. It also allows you to select the label and UI component used for each attribute. By default, ADF inputText
components are used, except for dates, which use the selectInputDate
component. Each inputText
component contains a validator tag that allows you to set up validation for the attribute. For more information, see Section 12.3, "Adding Validation".
You can elect to include navigational controls that allow users to navigate through all the data objects in the collection. For more information, see Section 6.4, "Incorporating Range Navigation into Forms". You can also elect to include a Submit button used to submit the form. This button submits the HTML form and applies the data in the form to the bindings as part of the JSF/ADF page lifecycle. For additional help in using the dialog, click Help. All UI components are placed inside a panelForm
component.
ADF Read-Only Form: Same as the ADF Form, but by default, outputText
components are used. Since the form is meant to display data, no validator
tags are added. The label
attribute is populated for each component. Attributes of type Date
also use the outputText
component. All components are placed inside panelLabelAndMessage
components, which are in turn placed inside a panelForm
component.
ADF Creation Form: Not to be used when using TopLink Java objects and an EJB session bean. Use constructors or custom methods instead. For more information, see Section 10.7, "Creating an Input Form for a New Record".
If you are building a form that allows users to update data, you now need to drag and drop a method that will perform the update. For more information, see Section 10.3, "Creating Command Components to Execute Methods".
Dropping an object as a form from the Data Control Palette has the same effect as dropping a single attribute, except that multiple attribute bindings and associated UI components are created. The attributes on the UI components (such as value
) are bound to properties on that attribute's binding object (such as inputValue
). Example 6-7 shows the code generated on the JSF page when you drop the ServiceRequest
collection for the findAllServiceRequest()
method as a default ADF Form.
Example 6-7 Code on a JSF Page for an Input Form
<af:panelForm> <af:inputText value="#{bindings.svrId.inputValue}" label="#{bindings.svrId.label}" required="#{bindings.svrId.mandatory}" columns="#{bindings.svrId.displayWidth}"> <af:validator binding="#{bindings.svrId.validator}"/> <f:convertNumber groupingUsed="false" pattern="#{bindings.svrId.format}"/> </af:inputText> <af:inputText value="#{bindings.status.inputValue}" label="#{bindings.status.label}" required="#{bindings.status.mandatory}" columns="#{bindings.status.displayWidth}"> <af:validator binding="#{bindings.status.validator}"/> </af:inputText> <af:selectInputDate value="#{bindings.requestDate.inputValue}" label="#{bindings.requestDate.label}" required="#{bindings.requestDate.mandatory}"> <af:validator binding="#{bindings.requestDate.validator}"/> <f:convertDateTime pattern="#{bindings.requestDate.format}"/> </af:selectInputDate> <af:inputText value="#{bindings.problemDescription.inputValue}" label="#{bindings.problemDescription.label}" required="#{bindings.problemDescription.mandatory}" columns="#{bindings.problemDescription.displayWidth}"> <af:validator binding="#{bindings.problemDescription.validator}"/> </af:inputText> <af:selectInputDate value="#{bindings.assignedDate.inputValue}" label="#{bindings.assignedDate.label}" required="#{bindings.assignedDate.mandatory}"> <af:validator binding="#{bindings.assignedDate.validator}"/> <f:convertDateTime pattern="#{bindings.assignedDate.format}"/> </af:selectInputDate> <f:facet name="footer"> <af:commandButton text="Submit"/> </f:facet> </af:panelForm>
Note:
For information regarding the validator and converter tag, see Chapter 12, "Using Validation and Conversion".JSF components use facet tags to hold other components that require a special relationship with the parent component, for example, headers and footers for columns within a table, or the footer
facet for the panelForm
component. When you use the Data Control Palette to drop a widget, any preferred facets are included.
Many components use facets, and when you use widgets to create complex components (such as panelForm
), output tags are often automatically created and inserted into the facets. You can manually edit these components or add other components to facets.
When you choose to include a Submit button when you drop a collection as an input form, a command button is added to the panelForm's
footer
facet. This command button causes the form that holds the panelForm
to be submitted. By default, the text is Submit. Figure 6-4 shows the command button in the panelForm's
footer
facet.
Example 6-8 shows the corresponding code in the JSF page.
Example 6-8 Facet in a JSF Page
<af:panelForm> ... <f:facet name="footer"> <af:commandButton text="Submit"/> </f:facet> </af:panelForm>
Each facet can hold only one component. If you need a facet to hold more than one component, then you need to nest those components in a container component, which can then be nested in the facet. For an example of how the panelGroup
and panelButtonBar
components are used to group all buttons in the footer
facet of a form, see Section 6.4.2.3, "Using EL Expressions to Bind to Navigation Operations".
Also note that JDeveloper displays all facets available to the component in the Structure window. However, only those that contain UI components appear activated. Any empty facets are "grayed" out. Figure 6-5 shows both full and empty facets for a panelPage
component
When you choose to add navigation when you use the Data Control Palette to create an input form, JDeveloper includes ADF Faces command components bound to existing navigational logic on the data control. This built-in logic allows the user to navigate through the data objects in the collection. Figure 6-6 shows a form that contains navigation buttons.
By default, when you choose to include navigation when creating a form using the Data Control Palette, JDeveloper creates First, Last, Previous, and Next buttons that allow the user to navigate within the collection.
You can also add navigation buttons to an existing form manually.
To manually add navigation buttons:
From the Data Control Palette, select the operation associated with the collection of objects on which you wish the operation to execute, and drag it onto the JSF page.
For example, if you want to navigate through service requests, you would drag the Next operation associated with the ServiceRequest
collection of the findAllServiceRequest()
method. Figure 6-7 shows the navigation operations associated with a collection.
Choose either Command Button or Command Link from the context menu.
When you drop any operation as a command component, JDeveloper:
Defines an action binding in the page definition file for the associated operations
Inserts code in the JSF page for the command components
Action bindings execute business logic. Action bindings can invoke methods on a business service (for example, the method action binding for a method used by an iterator to access a collection) or as in the case of navigation controls, they can invoke built-in methods on the action binding object. These built-in methods operate on the iterator or on the data control itself, and are represented as operations in the Data Control Palette. JDeveloper provides navigation operations that allow users to navigate forward, backwards, to the last object in the collection, and to the first object.
Note:
For more information about using methods to add, remove, or update data, see Chapter 10, "Creating Command Components to Execute Methods".Like value bindings, action bindings for navigation operations must also contain a reference to the iterator binding, as it is used to determine the current object and can therefore determine the correct object to display when each of the navigation buttons is clicked. Example 6-9 shows the action bindings for the navigation operations.
Tip:
The numerical values of theAction
attribute in the <action>
tags (as shown in Figure 6-7) are defined in the oracle.adf.model.meta.OperationDefinition
class. However, when you use the ADF Model layer's action binding editor, you never need to set the numerical code by hand.Example 6-9 Page Definition Code for an Operation Action Binding
<action id="First" RequiresUpdateModel="true" Action="12" IterBinding="findAllServiceRequestIterator"/> <action id="Previous" RequiresUpdateModel="true" Action="11" IterBinding="findAllServiceRequestIterator"/> <action id="Next" RequiresUpdateModel="true" Action="10" IterBinding="findAllServiceRequestIterator"/> <action id="Last" RequiresUpdateModel="true" Action="13" IterBinding="findAllServiceRequestIterator"/>
Iterator bindings have a rangeSize
attribute used to determine the number of data objects to return for each iteration. This attribute helps in situations when the number of objects in the data source is quite large. Instead of returning all objects, only a set number are returned and accessible by the other bindings. Once the iterator reaches the end of the range, it accesses another set, creating a new range. Example 6-10 shows the range size for the findAllServiceRequestIter
iterator.
Note:
ThisrangeSize
attribute is not the same as the row
attribute on a table component. For more information, see Table 7-1, "ADF Faces Table Attributes and Populated Values".Example 6-10 RangeSize Attribute for an Iterator
<executables>
<methodIterator id="findAllServiceRequestIter"
Binds="findAllServiceRequest.result"
DataControl="SRPublicFacade" RangeSize="10"
BeanClass="oracle.srdemo.model.entities.ServiceRequest"/>
</executables>
By default, the rangeSize
attribute is set to 10
. This means that a user can view 10 objects, navigating back and forth between them, without needing to access the data source. The iterator keeps track of the current object. Once a user clicks a button that requires a new range (for example, clicking the Next button on object number 10), the binding object executes its associated method against the iterator, and the iterator retrieves another set of 10 records. The bindings then work with that set. You can change this setting as needed. You can set it to -1
to have the full record set returned. The default is -1 for iterator bindings that furnish a list of valid choices for list bindings.
Tip:
You can also set a range of records directly in the query you write on your business service. However, doing so means every page that uses the query will return the same range size. By setting the range size on the iterator, you can control the size per page.Table 6-1 shows the built-in navigation operations provided on data controls, along with the action attribute value set in the page definition, and the result of invoking the operation or executing an event bound to the operation. For more information about action events, see Section 6.4.3, "What Happens at Runtime: About Action Events and Action Listeners".
Table 6-1 Built-in Navigation Operations
Operation | Action Attribute Value | When invoked, the associated iterator binding will... |
---|---|---|
Next |
10 |
Move its current pointer to the next object in the result set. If this object is outside the current range, the range is scrolled forward a number of objects equal to the range size. |
Previous |
11 |
Move its current pointer to the preceding object in the result set. If this object is outside the current range, the range is scrolled backward a number of objects equal to the range size. |
First |
12 |
Move its current pointer to the beginning of the result set. |
Last |
13 |
Move its current pointer to the end of the result set. |
Next Set |
14 |
Move the range forward a number of objects equal to the range size attribute. |
Previous Set |
15 |
Move the range backward a number of objects equal to the range size attribute. |
SetCurrentRowWithKey |
96 |
Set the row key as a |
SetCurrentRowWithKeyValue |
98 |
Set the current object on the iterator, given a key's value. |
Every action binding for an operation has an enabled
boolean property that the ADF framework sets to false
when the operation should not be invoked. You can then bind the UI component to this value to determine whether or not the component should be enabled. For more information about the enabled
property, see Appendix B, "Reference ADF Binding Properties".
When you create command components using navigation operations, the components are placed in a panelButtonBar
component. JDeveloper creates an EL expression that binds a navigational command button's actionListener
attribute to the execute
property of the action binding for the given operation. This expression causes the binding's operation to be invoked on the iterator when a user clicks the button.
For more information about the command button's actionListener
attribute, see Section 6.4.3, "What Happens at Runtime: About Action Events and Action Listeners". For example, the First command button's actionListener
attribute is bound to the execute
method on the First
action binding.
The disabled
attribute is used to determine if the button should be inactivated. For example, if the user is currently displaying the first record, the First button should not be able to be clicked. The code uses an EL expression that evaluates to the enabled
property on the action binding. If the property value is not true
(for example, if the current record is the first record, the First
operation should not be enabled), then the button is disabled. Example 6-11 shows the code generated on the JSF page for navigation operation buttons.
Example 6-11 JSF Code for Navigation Buttons Bound to ADF Operations
<f:facet name="footer"> <af:panelButtonBar> <af:commandButton actionListener="#{bindings.First.execute}" text="First" disabled="#{!bindings.First.enabled}"/> <af:commandButton actionListener="#{bindings.Previous.execute}" text="Previous" disabled="#{!bindings.Previous.enabled}"/> <af:commandButton actionListener="#{bindings.Next.execute}" text="Next" disabled="#{!bindings.Next.enabled}"/> <af:commandButton actionListener="#{bindings.Last.execute}" text="Last" disabled="#{!bindings.Last.enabled}"/> </af:panelButtonBar> <af:commandButton text="Submit"/> </f:facet>
An action event occurs when a command component is activated. For example, when a user clicks a button, the form the component is enclosed in is submitted, and subsequently an action event is fired. Action events might affect only the user interface (for example, a link to change the locale, causing different field prompts to display), or they might involve some logic processing in the back end (for example, a button to navigate to the next record).
An action listener is a class that wants to be notified when a command component fires an action event. An action listener contains an action listener method that processes the action event object passed to it by the command component.
In the case of the navigation operations, when a user clicks, for example, the Next button, an action event is fired. This event stores currency information about the current data object, taken from the iterator. Because the component's actionListener
attribute is bound to the execute
method of the Next
action binding, the Next
operation is invoked. This method takes the currency information passed in the event object to determine what the next data object should be.
When a user clicks the navigation buttons, the iterator determines the next data object to display. However, when the user clicks the browser's Back button, the action and/or event is not shared outside the browser, and the iterator is bypassed. Therefore, when a user clicks a browser's Back button instead of using navigation buttons on the page, the iterator becomes out of sync with the page displayed, causing unexpected results.
For example, say a user browses to object 103, and then uses the browser's Back button. Because the browser shows the page last visited, object 102 is shown. However, the iterator still thinks the current object is 103 because the iterator was bypassed. If the user were to then to click the Next button, object 104 would display because that is what the iterator believes to be the next object, and not 103 as the user would expect.
Because the iterator and the page are out of sync, problems can arise when a user edits records. For example, if the user were to have edited object 102 after clicking the browser's Back button, the changes would have actually been posted to 103, because this is what the iterator thought was the current object.
To prevent a user making changes to the wrong object instances, you can use token validation. When you enable token validation for a page, that page is annotated with the current object for all the iterators used to render that page. This annotation is encoded onto the HTML payload rendered to the browser and is submitted to the server along with any data. At that point, the current object of the iterator is compared with the annotation. If they are different, an exception is thrown.
For example, in the earlier scenario, when the user starts at 103 but then clicks the browser's Back button to go to 102, as before, the previous page is displayed. However, that page was (and still is) annotated with 102. Therefore, when the user clicks the Next button to submit the page and navigate forward, the annotation (102) does not match the iterator (which is still at 103), an exception is thrown, and the Next
operation is not executed. The page renders with 103, which is the object the iterator believed to be current. An error displays on the page stating that 102 was expected, since the server expected 102 based on the annotation submitted with the data. Since 103 is now displayed, both the annotation and the iterator are now at 103, and are back in sync.
Token validation is set on the page definition for a JSF page. By default, token validation is on.
To set token validation:
Open the page definition file for the page.
In the Structure window, select the root node for the page definition itself.
In the Property Inspector, use the dropdown list for the EnableTokenValidation
attribute to set validation to true to turn on token validation, or false to turn off token validation.
Example 6-12 shows a page definition file after token validation was set to true.
Once you use the Data Control Palette to create a form, you can then delete attributes, change the order in which they are displayed, change the component used to display them, and change the attribute to which they are bound.
You can modify certain aspects of the default components dropped from the Data Control Palette. You can use the Structure window to change the order in which components are displayed, to add new components or change existing components, or to delete components. You can use the Property Inspector to change or delete bindings, or to change the label displayed for a component.
To modify default components and bindings:
Use the Structure window to do the following:
Change the order of the UI components by dragging them up or down the tree. A black line with an arrowhead denotes where the UI component will be placed.
Add a UI component for a new attribute. Right-click an existing UI component in the Structure window and choose to place the new component before, after, or inside the selected component. You then choose from a list of UI components.
To bind the new component to an attribute, you need to use the Property Inspector. See the first bullet point in step 2 for details.
Delete a UI component. Right-click the component and choose Delete. If you wish to keep the component, but delete just the binding, you need to use the Property Inspector. See the second bullet point in step 2.
With the UI component selected in the Structure window, you can then do the following in the Property Inspector:
Add a binding for the UI component. Enter an EL expression in the Value field, or click the ellipsis (...) button in the Value field to open the EL Expression Builder. To select a binding available from the data control, select the ADF Bindings > Bindings node. This node shows the operations, iterators, and attributes available from the collection currently bound, as well as the binding properties. For more information about using EL expressions, see Section 5.6, "Creating ADF Data Binding EL Expressions".
Delete a binding for the UI component by deleting the EL expression.
Change the binding. You can rebind the component to any other attribute, or any property on another attribute. For procedures, see Section 6.5.1.1, "Changing the Value Binding for a UI Component".
Change the label for the UI component. By default, the label is bound to the binding's label
property (for more information about this property, see Appendix B, "Reference ADF Binding Properties".
You can also change the label just for the current page. To do so, select the Label
attribute. You can enter text or an EL expression to bind the label value to something else, for example, a key in a properties or resource file.
For example, the inputText
component used to enter the status of a service request would have the following for its Label
attribute:
#{bindings.status.label}
In this expression, status
is the ID for the attribute binding in the page definition file.
However, you could change the expression to instead bind to a key in a properties file, for example:
#{srproperties['sr.status']}
In this example, srproperties
is a variable defined in the JSF page used to load a properties file. The SREdit page uses a variable named res
. The label for the cancel button has the following value:
#{res['srdemo.cancel']}
For more information about using resource bundles, see Section 14.4, "Internationalizing Your Application".
Instead of modifying a binding, you can completely change the object to which the UI component in a form is bound.
To rebind a UI component:
From the Data Control palette, drag the collection or attribute that you now want the component to be bound to, and drop it on the component.
OR
Right-click the UI component in the Structure window and choose Edit Binding. Either the Attribute, Table, or List Binding Editor launches, depending on the UI component for which you are changing the binding.
In the context menu, select Bind existing <component name>.
When a component is bound to a built-in operation, you can change the action using the Action Binding Editor.
To rebind a UI Command component:
Right-click the command component in the Structure window and choose Edit Binding, which launches the Action Binding Editor.
In the editor, use the dropdown menu to select a different action.
When you modify how an attribute is displayed by moving the UI component or changing the UI component, JDeveloper changes the corresponding code on the JSF page. When you use the binding editors to add or change a binding, JDeveloper adds the code to the JSF page, and also adds the appropriate elements to the page definition file.