Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g Release 3 (10.1.3.0) Part Number B25947-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. You can create forms that display values, forms that allow users to edit values, forms that collect values (input forms), and search forms.
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 create different types of forms, such as forms that allow you to edit existing objects or create new ones.
How to add operations that allow you to navigate between the data objects displayed in a form, or that allow you to manipulate data.
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 12.2.1, "How to Understand the Items on the Data Control Palette").
For example, Figure 13-1 shows the ProblemDescription
attribute under the ServiceRequests
collection of the SRService
data control in the SRDemo application. This is the attribute to drop to display or enter the problem description for a service request.
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 20, "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 19.7, "Creating Selection 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 directory name, and appending PageDef
as the name of the page definition file. For example, the page definition file for the SREdit page in the ./app/staff subdirectory of the web root is app_staff_SREditPageDef.xml
. For a complete account of what happens when you drag an attribute onto a page, see Section 12.2.3, "What Happens When You Use the Data Control Palette". Bindings for the iterator and attributes are created and added to the page definition file. 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 an iterator binding if it does not already exist. An iterator binding references an iterator for the data collection, which facilities 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 specify 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. Note that the iterator binding is not an iterator. it is a binding to an iterator. In the case of ADF Business Components, the actual iterator is the default row set iterator for the default row set of the view object instance in the application module's data model.
Tip:
There is one iterator binding 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 binding. This is fine, unless you need the binding to behave differently for the different components. In that case, you will need to manually create separate iterator bindings. For procedures and an example, see Section 18.5, "Conditionally Displaying the Results Table on a Search Page".For example, if you drop the ProblemDescription
attribute under the ServiceRequests
collection, JDeveloper creates an iterator binding for the ServiceRequests
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 13.4.2.2, "Iterator RangeSize Attribute". Example 13-1 shows the iterator binding created when you drop an attribute from the ServiceRequests
collection.
Example 13-1 Page Definition Code for an Iterator Binding When You Drop an Attribute from a Collection
<executables> <iterator id="ServiceRequestsIterator" RangeSize="10" Binds="ServiceRequests" DataControl="SRService"/> </executables>
For information regarding the iterator binding element attributes, see Section A.2.2, "Oracle ADF Data Binding Files".
This metadata allows the ADF binding container to access the attribute. Because the iterator binding is an executable, it is invoked when the page is loaded, thereby allowing the iterator to access and iterate over the ServiceRequests
collection. This means that the iterator will manage all the service requests in the collection, including determining the current service request or range of service requests.
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
s collection as an ADF Output Text w/Label widget onto a page, JDeveloper creates an attribute binding for the ProblemDescription
attribute. This allows the binding to access the attribute value of the current record. Example 13-2 shows the attribute binding for ProblemDescription
created when you drop the attribute from the ServiceRequests
collection. Note that the attribute value references the iterator named ServiceRequests
Iterator
.
Example 13-2 Page Definition Code for an Attribute Binding
<bindings> ... <attributeValues id="ServiceRequestsProblemDescription" IterBinding="ServiceRequestsIterator"> <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 13-3 shows the code generated on the JSF page when you drop the ProblemDescription
attribute as an Output Text w/Label widget.
Example 13-3 JSF Page Code for an Attribute Dropped as an Output Text w/Label
<af:panelLabelAndMessage label="#{bindings.ServiceRequestsProblemDescription.label}"> <af:outputText value="#{bindings.ServiceRequestsProblemDescription.inputValue}"/> </af:panelLabelAndMessage>
If instead you drop the ProblemDescription
attribute as an Input Text w/Label widget, JDeveloper creates an inputText
component. As Example 13-4 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 20.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 13-4 JSF Page Code for an Attribute Dropped as an Input Text w/Label
<af:inputText value="#{bindings.ServiceRequestsProblemDescription.inputValue}" label="#{bindings.ServiceRequestsProblemDescription.label}" required="#{bindings.ServiceRequestsProblemDescription.mandatory}" columns="#{bindings.ServiceRequestsProblemDescription.displayWidth}"> <af:validator binding="#{bindings.ServiceRequestsProblemDescription.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 13-5 shows the ADF PhaseListener configuration in faces-config.xml
.
Example 13-5 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 13-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 Section 10.5.5.1, "Correctly Configuring the Refresh Property of Iterator Bindings". For details about the refresh attribute, see Section A.6.1, "PageDef.xml Syntax".
In the problem description example, the bindingContainer
invokes the ServiceRequestsIterator
iterator, which returns the ServiceRequests
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 13-2), and since the UI component is bound to the ProblemDescription
binding using an EL expression (#{bindings.problemDescription.inputValue}
), that data can be 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 20, "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 20, "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 16.4, "Using Dynamic Navigation". For a description of action bindings used to invoke business logic, see Section 13.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 Section 10.5.5.1, "Correctly Configuring the Refresh Property of Iterator Bindings".
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 ServiceRequests
collection, which contains all the attributes necessary to edit a given service request.
You can also create forms that provide more functionality than displaying data from a collection. For information about creating a form that allows a user to update data, see Section 13.5, "Creating a Form to Edit an Existing Record". For information about creating forms that allow users to create a new object for the collection, see Section 13.6, "Creating an Input Form". You can also create search forms. For more information, see Chapter 18, "Creating a Search Form".
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.
To create a basic form:
From the Data Control Palette, select the collection that represents the data you wish to display. Figure 13-3 shows the ServiceRequests
collection for the SRService
data control.
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 20.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 13.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 Search Form: Creates a form that can be used to execute a Query-By-Example (QBE) search. For more information, see Chapter 18, "Creating a Search Form".
ADF Creation Form: Creates an input form that allows users to create a new instance for the collection. For more information, see Section 13.6, "Creating an Input Form".
If you are building a form that allows users to update data, you now need to drag and drop an operation that will perform the update. For more information, see Section 13.5, "Creating a Form to Edit an Existing Record".
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 13-6 shows the code generated on the JSF page when you drop the ServiceRequests
collection as a default ADF Form.
Example 13-6 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}"/> </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:panelForm>
Note:
For information regarding the validator and converter tag, see Chapter 20, "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 13-4 shows the command button in the panelForm's
footer
facet.
Example 13-7 shows the corresponding code in the JSF page.
Example 13-7 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 13.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 13-5 shows both full and empty facets for a panelPage
component
When you create an ADF Form, you are given the option of adding navigation. If you choose to do so, 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 all the data objects in the collection. Figure 13-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 ServiceRequests
collection. Figure 13-7 shows the 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. For example, 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.
Like value bindings, action bindings for operations will contain a reference to the iterator binding when the action binding is bound to one of the iterator-level actions, such as Next
or Previous
, 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 (action bindings to other than iterator-level actions, for example for a custom method on an AM, or for the commit or rollback operations, will not contain this reference). Example 13-8 shows the action bindings for the navigation operations.
Tip:
The numerical values of theAction
attribute in the <action>
tags (as shown in Figure 13-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 13-8 Page Definition Code for an Operation Action Binding
<action id="First" RequiresUpdateModel="true" Action="12" IterBinding="ServiceRequestsIterator"/> <action id="Previous" RequiresUpdateModel="true" Action="11" IterBinding="ServiceRequestsIterator"/> <action id="Next" RequiresUpdateModel="true" Action="10" IterBinding="ServiceRequestsIterator"/> <action id="Last" RequiresUpdateModel="true" Action="13" IterBinding="ServiceRequestsIterator"/>
Iterator bindings have a rangeSize
attribute used to determine the number of data objects to make available for the page 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 the next set. Example 13-9 shows the default range size for the ServiceRequestsIterator
iterator.
Note:
ThisrangeSize
attribute is not the same as the row
attribute on a table component. For more information, see Table 14-1, "ADF Faces Table Attributes and Populated Values".Example 13-9 RangeSize Attribute for an Iterator
<executables> <iterator id="ServiceRequestsIterator" RangeSize="10" Binds="ServiceRequests" DataControl="SRService"/> </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.
Table 13-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 13.4.3, "What Happens at Runtime: About Action Events and Action Listeners".
Table 13-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. |
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 13.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 13-10 shows the code generated on the JSF page for navigation operation buttons.
Example 13-10 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 13-11 shows a page definition file after token validation was set to true.
You can create a form that allows a user to edit the current data, and then commit those changes to the data source. You then use a operations associated with a collection or the data control itself to create command buttons that can be used to modify data records. For example, you use the Delete
operation to create a button that allows a user to delete a record from the current range. Or you can use the built-in Submit button to submit changes.
Tip:
You can also use the Create operation on a form to create a new object, however using the ADF Creation Form provides additional built-in functionality. See Section 13.6, "Creating an Input Form" for more information.It is important to note that these operations are executed only against objects in the ADF cache. You need to use the Commit
operation on the root data control to actually commit any changes to the data source. You use the data control's Rollback
operation to rollback any changes made to the cached object.
To use the operations on a form, you follow the same procedures as the navigation operations (see Section 13.4.1, "How to Insert Navigation Controls into a Form" for procedures), however you must also create the buttons for the commit and rollback operations in order for changes to be committed to the data store or to restore the cache.
To create an edit form:
From the Data Control Palette, drag the collection for which you wish to create the form, and select ADF Form from the context menu.
In the Edit Form Fields dialog, if you want the user to be able to change data, select Include Submit Button.
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 be able delete service requests, you would drag the Delete
operation associated with the ServiceRequests
collection. Figure 13-8 shows the operations associated with a collection.
Choose either Command Button or Command Link from the context menu.
From the Data Control Palette, drag the Commit
and Rollback
operations associated with the root level data control, and drop them as either a command button or command link. This will allow the changes to be committed to or rolled back. Figure 13-9 shows the commit and rollback operations for the SRService
data control.
Dropping any data control operation as a command button causes the same events as dropping navigation operations. See Section 13.4.2, "What Happens When Command Buttons Are Created Using the Data Control Palette" for more information.
The only difference is that the action bindings for the Commit
and Rollback
operations do not require a reference to the iterator, as they execute a method on the application module. Example 13-12 shows the action bindings generated in the page definition file for these operations.
Example 13-12 Action Bindings for Commit and Rollback Operations
<action id="Commit" InstanceName="SRService" DataControl="SRService" RequiresUpdateModel="true" Action="100"/> <action id="Rollback" InstanceName="SRService" DataControl="SRService" RequiresUpdateModel="false" Action="101"/>
The following table shows the built-in non-navigation operations provided on data controls, along with the result of invoking the operation or executing an event bound to the operation (see Section 13.4.3, "What Happens at Runtime: About Action Events and Action Listeners" for more information about action events).
Table 13-2 More Built-in Operations
Operation | Action Attribute Value | When invoked, the associated iterator binding will... |
---|---|---|
Create |
41 |
Creates a row directly before the current row, then moves the current row pointer to the new row. Note that the range does not move, meaning that the last row in the range may now be excluded from the range. Also note that this performs a |
Delete |
30 |
Deletes the current row from the cache and moves the current row pointer to the next row in the result set. Note that the range does not move, meaning that a row may be added to the end of the range. If the last row is deleted, the current row pointer moves to the preceding row. If there are no more rows in the collection, the enabled attribute is set to "disabled." |
SetCurrentRowWithKey |
96 |
Set the row key as a |
SetCurrentRowWithKeyValue |
98 |
Set the current object on the iterator, given a key's value. |
RemoveRowWithKey |
99 |
Uses the row key as a |
Commit |
100 |
Causes all items currently in the cache to be committed to the database. |
Rollback |
101 |
Clears the cache and returns the transaction and iterator to the initial state. |
Execute and Find |
These operations are used only in search forms. See Chapter 18, "Creating a Search Form" for more information. |
You can create a form that allows a user to enter information for a new record and then commit that record into the data source. While you can choose to use the default ADF Form and then drop the Create operation as a command button, when this type of form is first rendered, it displays the data for the first instance in the collection. The ADF Creation form allows users to create new instances in the collection without first displaying existing instances.
There may be times, however, when you need more control over how a new object is created. For example, you may want certain attributes to be populated programmatically. In this case, you might create a custom method on your application module to handle the creation of objects. To use a custom method to create an input form, instead of using an ADF Creation form, you use a standard form and then drop a custom method as a command button. The custom method will execute when the user clicks the button.
Note:
When you create a row programmatically, you should call theNewRow.setNewRowState(RowSet.STATUS_INITIALIZED
method to get the same behavior as the built-in Create
action. For more information, see Section 10.4.4, "What You May Need to Know About Create and CreateInsert".You create an input form similar to the way you create any other form. However, by selecting an ADF Creation Form when dropping a collection, JDeveloper provides additional functionality automatically.
To create an input form:
From the Data Control Palette, drag the collection for which you wish to create the form, and select ADF Creation Form from the context menu.
In the Edit Form Fields dialog, do not include the Submit button.
From the Data Control Palette, drag the Commit and Rollback operations associated with the root data control, and drop them as command buttons or links.
In the Structure window, select the command button for the commit operation.
In the Property Inspector, set the command button's Disabled property to False
.
By default, JDeveloper binds the Disabled
attribute of the button to the Enabled
property of the binding, causing the button to be disabled when the Enabled
property is set to False
. For this binding, the Enabled
property is false
until an update has been posted. For the purposes of an input form, the button should always be enabled, since there will be no changes posted before the user needs to create the new object.
When you use an ADF Creation Form to create an input form, JDeveloper:
Creates an iterator binding for the collection, an action binding for the Create operation, and attribute bindings for each of the attributes of the object in the collection. It also creates an invoke action in the executables section of the page definition that causes the Create
operation to execute during the Render Model phase. If you created command buttons or links using the Commit
and Rollback
operations, JDeveloper also creates an action bindings for those operations.
Inserts code in the JSF page for the form using ADF Faces inputText
components, and in the case of the operations, commandButton
components.
For example, to create a simple input form for products in the SRDemo application, you might drop the ProductsList
collection from the Data Control Palette as an ADF Creation Form, and then drop the Commit operation as a button below the form, as shown in Figure 13-10.
Note:
This page is an example only and does not exist in the SRDemo application.Example 13-13 shows the page definition file for this input form. When the invoke action executes during the prepare render lifecycle phase, the Create
operation is invoked, and a new instance for the collection is created. For more information about the lifecycle, see Chapter 13, "What Happens at Runtime: The JSF and ADF Lifecycles"
Example 13-13 Page Definition Code for a Creation Form
<executables> <iterator id="ProductListIterator" RangeSize="10" Binds="ProductList" DataControl="SRService"/> <invokeAction Binds="Create" id="invokeCreate" Refresh="renderModel" RefreshCondition="${!adfFacesContext.postback and empty bindings.exceptionsList}"/> </executables> <bindings> <action id="Create" RequiresUpdateModel="true" Action="41" IterBinding="ProductListIterator" InstanceName="SRServiceDataControl.ProductList" DataControl="SRServiceDataControl"/> <attributeValues id="ProdId" IterBinding="ProductListIterator"> <AttrNames> <Item Value="ProdId"/> </AttrNames> </attributeValues> <attributeValues id="Name" IterBinding="ProductListIterator"> <AttrNames> <Item Value="Name"/> </AttrNames> </attributeValues> <attributeValues id="Description" IterBinding="ProductListIterator"> <AttrNames> <Item Value="Description"/> </AttrNames> </attributeValues> <action id="Commit" InstanceName="SRServiceDataControl" DataControl="SRServiceDataControl" RequiresUpdateModel="true" Action="100"/> </bindings>
Anytime the Create
operation is invoked (for example, when the page is first rendered), an data object is created. However, since data is cached (which allows the Create
operation to do a postback to the server), and since the Create
operation is invoked whenever the page enters the renderModel
phase, the Create
operation will create the same object again when the user revisits the page, perhaps to create another object. Additionally, if errors occur, when the page is rerendered with the error message, the object would again be created.
To prevent duplicates, the invoke action's refreshCondition
property is set so that the Create operation will only be invoked whenever there has been no postback to the server and as long as there are no error messages (see Example 13-13 for the EL expression). When the user clicks the Commit button (which is bound to the Commit action through an action binding), the new object with the data is submitted to the data source. If you do want the user to be able to stay on the same page to create multiple objects, see Chapter 13, "What You May Need to Know About Create Forms and the RefreshCondition".
Because the invokeCreate
action is executed before the page is displayed, if you are populating the primary key (in this case the Product ID) using sequences, that number will appear in the input text field. For example, instead of being blank, the Product ID in Figure 13-10 displays the product ID number. This is because the product entity class contains a method that uses an eager fetch to generate a sequence of numbers for the prodId
attribute. This populates the value as the row is created.
However, if instead you've configured the attribute's type to DBSequence (which uses a database trigger to generate the sequence), the number would not be populated until the object is committed to the database. In this case, the user would see a negative number as a placeholder. To avoid this, you can use the following EL expression for the Rendered
attribute of the input text field:
#{bindings.EmployeeId.inputValue.value > 0}
This expression will display the component only when the value is greater than zero, which will not be the case before it is committed. Similarly, you can simply set the Rendered
attribute to false
. However, then the page will never display the input text field component for the primary key.
If the Commit button for the input form does not cause the user to navigate off the page, the page re-renders displaying the data from the last entry. This is because the refreshCondition
on the invokeCreate
invoke action (${!adfFacesContext.postback and empty bindings.exceptionsList}
) causes the action to not be invoked when there is a postback, which is the case when committing the data.
If you want users to be able to stay on the page (for example to create more than one product at a time), you need to change the refreshCondition
so that it will allow the invokeCreate
action to execute after committing a new object. To do this, you use the setActionListener
component within the Commit command component. The setActionListener
component allows the command component to set a value before navigating. In this case, the command component will set a flag (named createAnother
) on the request scope to true
. The refreshCondition
can then evaluate this flag to determine whether or not to execute the invokeCreate
action, as shown in the following procedure. For more information about the setActionListener
component, see Section 17.4.2, "What Happens When You Set Parameters".
To create an input form that can create multiple objects:
Follow the procedure to create an input form.
On the JSF page, drag a setActionListener
component from the Component Palette onto the Commit command button.
Note:
This procedure assumes that the user will click this Commit button and return to the same page to create another entry. You may wish to change the name of the button to reflect that, for example, Commit and Create Another. You will need to create a different button to navigate off the page.In the Insert setActionListener dialog, set the following:
From: #{true}
To: #{requestScope.createAnother}
This creates a flag named createAnother
on the request scope, and sets it to true
.
Open the page definition for the JSF page.
Change the refreshCondition
for the invokeCreate
invoke action from
${!adfFacesContext.postback and empty bindings.exceptionsList}
to:
#{empty bindings.expressionList and (!adfFacesContext.postback or requestScope.createAnother)}
This sets the condition to execute the invokeCreate
action when the expression list that contains error messages is empty, and there was not or postback or the createAnother
flag on the request scope is set to true.
Note:
ThesetActionListener
component executes during the Invoke Application phase of the lifecycle. Therefore, the refresh
attribute for the invokeCreate
action must be set to a subsequent phase. In this case, it is set to renderModel
by default, and should not be changed.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 12.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 13.7.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". This property allows your page to use the UI control hints for labels that you have defined for your entity object attributes or view object attributes, where you can change the value once and have it appear the same on all pages that display the label.
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 22.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.