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 add more complex bindings to your pages, such as using methods that take parameters to create forms and command components.
This chapter includes the following sections:
Section 17.3, "Creating Command Components to Execute Methods"
Section 17.4, "Setting Parameter Values Using a Command Component"
Once you create a basic page and add navigation capabilities, you may want to add more complex features to your pages, such as the ability to store information on a managed bean, or the ability to override declarative actions. ADF provides many features that allow you to add this complex functionality using very little actual code.
For example, the Delete button on the SRMain page was created by dragging the Delete
operation for the ServiceRequest
collection and dropping it as a command button. Then that Delete
operation was overridden, so that when the button is clicked, it not only deletes the service request from the cache, but also commits the transaction. Additionally, the SRMain page can be accessed from a number of pages in the SRDemo application. The userState
managed bean (UserSystemState.java
) holds the value of the originating page. The overridden Delete
operation also contains logic to access the value of the originating page so that the user navigates successfully off the SRMain page.
Read this chapter to understand:
How to create an use a managed bean to store parameter values or flags
How to create command components that will invoke custom methods on your application module
How to set parameter values
How to add logic to an operation or custom method bound to a command component
Often, pages require information from other pages. Instead of setting this information directly on a page (for example, by setting the parameter value on the page's page definition file), which essentially hardcodes the information, you can store this information on a managed bean. As long as the bean is stored in a scope that is accessible, any value for an attribute on the bean can be accessed using an EL expression.
Tip:
Use managed beans to store only "bookeeping" information. All application data and processing should be handled by logic in the business layer of the application.For example, the SRMain page needs to know the page from which the user navigated from in order to return the user to the correct page. The SRDemo application has a managed bean that holds this information, allowing the sending page to set the information, and the SRMain page to use the information in order to determine where to navigate.
Managed beans are Java classes that you register with the application using the faces-config.xml
file. When the JSF application starts up, it parses this configuration file and the beans are made available and can be referenced in an EL expression, allowing access to the beans' properties and methods. Whenever a managed bean is referenced for the first time and it does not already exist, the Managed Bean Creation Facility instantiates the bean by calling the default constructor method on the bean. If any properties are also declared, they are populated with the declared default values.
Using the JSF Configuration Editor in JDeveloper, you can create a managed bean and register it with the JSF application at the same time.
To create a managed bean:
Open the faces-config.xml
file. This file is stored in the <project_name>/WEB-INF
directory.
At the bottom of the window, select the Overview tab.
In the element list on the left, select Managed Beans. Figure 17-1 shows the JSF Configuration Editor for the faces-config.xml file.
Click the New button to open the Create Managed Bean dialog, as shown in Figure 17-2. Enter the name and fully qualified class path for the bean. Select a scope, select the Generate Java File checkbox, and click OK.
Tip:
If the managed bean will be used by multiple pages in the application, you should set the scope toSession
. However, then the bean cannot contain any reference to the binding container, as the data on the binding object is on the request
scope, and therefore cannot "live" beyond a request. For examples of when you may need to reference the binding container, see Section 17.5, "Overriding Declarative Methods".You can optionally use the arrow to the left of the Managed Properties bar to display the properties for the bean. Click New to create any properties. Press F1 for additional help with the configuration editor.
Note:
While you can declare managed properties using the configuration editor, the corresponding code is not generated on the Java class. You will need to add that code by creating private member fields of the appropriate type and then using the Generate Accessors... menu item on the context menu of the code editor to generate the corresponding getter and setter methods for these bean properties.When you use the configuration editor to create a managed bean, and elect to generate the Java file, JDeveloper creates a stub class with the given name and a default constructor. Example 17-1 shows the code added to the MyBean
class stored in the view package.
Example 17-1 Generated Code for a Managed Bean
package view; public class MyBean { public MyBean() { } }
JDeveloper also adds a managed-bean
element to the faces-config.xml
file. This declaration allows you to easily access any logic on the bean using an EL expression that refers to the given name. Example 17-2 shows the managed-bean
element created for the MyBean
class.
Example 17-2 Managed Bean Configuration on the faces-config.xml File
<managed-bean> <managed-bean-name>my_bean</managed-bean-name> <managed-bean-class>view.MyBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
You now need to add the logic required by your pages and methods in your application. You can then refer to that logic using an EL expression that refers to the managed-bean-name
given to the managed bean. For example, to access the myInfo
property on the bean, the EL expression would be:
#{my_bean.myInfo}
The following sections of this chapter provide examples of using the SRDemo application's userState
managed bean (view.UserSystemState.java
) to hold or get information. Please see those sections for more detailed examples of using a managed bean to hold information.
When your application contains custom methods, these methods appear in the Data Control Palette. You can then drag these methods and drop them as command buttons. When a user clicks the button, the method is executed. For more information about custom methods, see Chapter 8, "Implementing Business Services with Application Modules".
For example, the SRService application module in the SRDemo application contains the deleteServiceHistoryNotes(Set keySet)
method. This method ensures that one or more rows are selected, then deletes those rows and commits the transaction. To allow the user to execute this method, you drag the deleteServicehistoryNotes(Set)
method from the Data Control Palette, as shown in Figure 17-3.
Note:
In the SRDemo application, additional view-layer logic is added to thedeleteServiceHistoryNotes(Set)
method by overriding this method in a managed bean. You can also override a custom method if you need to provide conditional navigational logic. For more information, see Section 17.5, "Overriding Declarative Methods".In order to perform the required business logic, many methods require a value for the method's parameter or parameters. That means when you create a button bound to the method, you need to specify from where the value for the parameter(s) can be retrieved.
For example, if you use the deleteServicehistoryNotes(Set)
method, you need to specify the set of rows to be deleted.
To add a button bound to a method:
From the Data Control Palette, drag the method onto the page.
Tip:
If you are dropping a button for a method that needs to work with data in a table or form, that button must be dropped inside the table or form.From the context menu, choose Methods > ADF Command Button.
If the method takes parameters, the Action Binding dialog opens. In the Action Binding Editor, click the ellipses (...) button in the Value column of Parameters to launch the EL Expression Builder. You use the builder to set the value of the method's parameter.
When you drop a method as a command button, JDeveloper:
Defines a method action binding for the method. If the method takes any parameters, JDeveloper creates NamedData
elements that hold the parameter values.
Inserts code in the JSF page for the ADF Faces command component. This code is the same as code for any other command button, as described in Section 13.4.2.3, "Using EL Expressions to Bind to Navigation Operations". However, instead of being bound to the execute
method of the action binding for a built-in operation, the button is bound to the execute
method of the method action binding for the method that was dropped.
When you drop a method that takes parameters onto a JSF page, JDeveloper creates a method action binding. This binding is what causes the method to be executed when a user clicks the command component. When the method requires parameters to run, JDeveloper also creates NamedData
elements for each parameter. These elements represent the parameters of the method.
For example, the deleteServiceHistoryNotes
method action binding contains a NamedData
element for the Set
parameter. This element is bound to the value specified when you created the action binding. Example 17-3 shows the method action binding created when you drop the deleteServiceHistoryNotes
(Set)
method, and bind the Set
parameter (named keySet
) to the keySet
property of the selectionState
property of the history table UI component in the page's backing bean (for more information about using this method to delete multiple rows (the key set), see Section 14.6.5, "How to Use the TableSelectMany Component in the Selection Facet").
Example 17-3 Method Action Binding for a Parameter Method
<methodAction id="deleteServiceHistoryNotes" InstanceName="SRService.dataProvider" DataControl="SRService" MethodName="deleteServiceHistoryNotes" RequiresUpdateModel="true" Action="999"> <NamedData NDName="keySet" NDValue="${backing_SRMain.historyTable.selectionState.keySet}" NDType="java.util.Set"/> </methodAction>
Like creating command buttons using operations, when you create a command button using a method, JDeveloper binds the button to the method using the actionListener
attribute. The button is bound to the execute
property of the action binding for the given method. This binding causes the binding's method to be invoked on the application module. 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".
Tip:
Instead of binding a button to theexecute
method on the action binding, you can bind the button to method in a backing bean that overrides the execute
method. Doing so allows you to add logic before or after the original method runs. For more information, see Section 17.5, "Overriding Declarative Methods".Like navigation operations, the disabled
property on the button uses an EL expression to determine whether or not to display the button. Example 17-4 shows the EL expression used to bind the command button to the deleteServiceHistoryNotes(Set)
method.
Example 17-4 JSF Code to Bind a Command Button to a Method
<af:commandButton actionListener="#{bindings.deleteServiceHistoryNotes.execute}" text="deleteServiceHistoryNotes" disabled="#{!bindings.deleteServiceHistoryNotes.enabled}"/>
Tip:
When you drop a UI component onto the page, JDeveloper automatically gives it an ID based on the number of that component previously dropped, for example,commandButton1
, commandButton2
. You may want to change the ID to something more descriptive, especially if you will need to refer to it in a backing bean that contains methods for multiple UI components on the page. Note that if you do change the ID, you must manually update any references to it in EL expressions in the page.When the user clicks the button, the method binding causes the associated method to be invoked, passing in the value bound to the NamedData
element as the parameter. For example, if a user clicks a button bound to the deleteServiceHistoryNotes(Set)
method, the method takes the value of the key set (in this case the selected rows in a table) and deletes them from the data source.
There may be cases where an action on one page needs to set parameters that will be used to determine application functionality. For example, the results table on the SRSearch page will display only if the value of the searchFirstTime
flag on the userState
managed bean is false
. When this bean is instantiated as the search page is rendered, the isSearchFirstTime
method on the bean checks that parameter. If it is null
(which it will be the first time the page is rendered), the bean sets the value to true
.
A setActionListener
component, which is nested in the command button used to execute this search, is then used to set the searchFirstTime flag to false
, thus causing the results table to display once the search is executed. For information about using managed beans, see Section 17.2.1, "How to Use a Managed Bean to Store Information".
You can use the setActionListener
component to set values on other objects. This component must be a child to a command component.
To use the setActionListener component:
Create a command component using either the Data Control Palette or the Component Palette.
From the Component Palette, drag a setActionListener
component and drop it as a child to the command component.
In the Insert ActionListener dialog, set From to be the parameter value.
Set To to be where you want to set the parameter value.
Tip:
Consider storing the parameter value on a managed bean or in scope instead of setting it directly on the resulting page's page definition file. By setting it directly on the next page, you lose the ability to easily change navigation in the future. For more information, see Section 17.2, "Using a Managed Bean to Store Information". Additionally, the data in a binding container is valid only during the request in which the container was prepared. Therefore, the data may change between the time you set it and the time the next page is renderedThe setActionListener
component lets the command component set a value before navigating. When you set the From
attribute to the source of the value you need to pass, or the actual value, the component will be able to access that value. When you set the To
attribute to a target, the command component is able to set the value on the target. Example 17-5 shows the code on the JSF page for a command component that takes the value false
and sets that as the value of the searchFirstTime
flag on the userState
managed bean.
When a user clicks the command component, before navigation occurs, the setActionListener
component sets the parameter value. In Example 17-5, the setActionListener
takes the value false
and sets it as the value for the searchFirstTime
attribute on the userState
managed bean. Now, any component that needs to know this value in determining whether or not to render, can access it using the EL expression #{userState.searchFirstTime}
. For the complete example Section 18.5, "Conditionally Displaying the Results Table on a Search Page".
When you drop an operation or method as a command button, JDeveloper binds the button to the execute
method for the operation or method. However, there may be occasions when you need to add logic before or after the logic executes. For example, after the Delete
operation successfully executes, you may want to execute the Commit
operation, so that the user does not have to click another button.
JDeveloper allows you to add logic to a declarative operation by creating a new method and property on a managed bean that provide access to the binding container. By default, this generated code executes the operation or method. You can then add logic before or after this code. JDeveloper automatically binds the command component to this new method instead of the execute
property on the original operation or method. Now when the user clicks the button, the new method is executed.
Following are some of the instances in the SRDemo application where backing beans contain methods that inject the binding container and then add logic before or after the declarative method is executed:
SRCreateConfirm.java
: The createSR_action
method overrides the createServiceRequest
method to set the service request's ID as a parameter value on the JSFUtils
helper class after the method is executed.
SRMain.java
: The onDeleteRequest
method overrides the Delete
operation. It first executes the Delete
action. If any errors occur, it redisplays the page. If there are no errors, it executes the Commit
operation on the application module. If any errors occur, it redisplays the page. If there are no errors, the user is returned to the page that is held as the ReturnNavigationRule
value on the UserState
managed bean.
In order to override a declarative method, you must have a managed bean to hold the new method to which the command component will be bound. If your page has a backing bean associated with it, JDeveloper adds the code needed to access the binding object to this backing bean. If your page does not have a backing bean, JDeveloper asks you to create one.
Note:
You cannot use the following procedure if the command component currently has an EL expression as its value for theAction
attribute, as JDeveloper will not overwrite an EL expression. You must remove this value before continuing.To override a declarative method:
Drag the operation or method to be overridden onto the JSF page and drop it as a UI command component.
Doing so creates the component and binds it to the associated binding object in the ADF Model layer using the ActionListener
attribute on the component.
For more information about creating command components using methods on the Data Control Palette, see Section 17.3, "Creating Command Components to Execute Methods".
For more information about creating command components from operations, see Section 13.4.2, "What Happens When Command Buttons Are Created Using the Data Control Palette"
On the JSF page, double-click on the component.
In the Bind Action Property dialog, identify the backing bean and the method to which you want to bind the component using one of the following techniques:
If auto-binding has been enabled on the page, the backing bean is already selected for you, as shown in Figure 17-4.
To create a new method, enter a name for the method in the Method field, which initially displays a default name.
OR
To use an existing method, select a method from the dropdown list in the Method field.
Select Generate ADF Binding Code.
If the page is not using auto-binding, you can select from an existing backing bean or create a new one, as shown in Figure 17-5. For more information about auto-binding, see Section 11.5.4, "How to Use the Automatic Component Binding Feature".
Click New to create a new backing bean. The Create Managed Bean dialog is displayed. Use this dialog to name the bean and the class, and set the bean's scope.
OR
Select an existing backing bean and method from the dropdown lists.
Note:
If you are creating a new managed bean, then you must set the scope of the bean torequest
. Setting the scope to request
is required because the data in the binding container object that will be referenced by the generated code is on the request
scope, and therefore cannot "live" beyond a request.
Additionally, JDeveloper understands that the button is bound to the execute
property of a binding whenever there is a value for the ActionListener
attribute on the command component. Therefore, if you have removed that binding, you will not be given the choice to generate the ADF binding code. You need to either inject the code manually, or to set a dummy value for the ActionListener
before double-clicking on the command component.
After identifying the backing bean and method, click OK in the Bind Action Property dialog
JDeveloper opens the managed bean in the source editor. Example 17-6 shows the code inserted into the bean. In this example, a command button is bound to the Delete operation.
Example 17-6 Generated Code in a Backing Bean to Access the Binding Object
public BindingContainer getBindings() { return this.bindings; } public void setBindings(BindingContainer bindings) { this.bindings = bindings; } public String commandButton_action1() { BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("Delete"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } return null; }
You can now add logic either before or after the binding object is accessed.
Tip:
To get the result of an EL expression, you need to use theValueBinding
class, as shown in Example 17-7Example 17-7 Accessing the Result of an EL Expression in a Managed Bean
FacesContext fc = FacesContext.getCurrentInstance(); ValueBinding expr = fc.getApplication(). createValueBinding("#{bindings.SomeAttrBinding.inputValue}"); DCIteratorBinding ib = (DCIteratorBinding) expr.getValue(fc);
In addition to any processing logic, you may also want to write conditional logic to return one of multiple outcomes depending on certain criteria. For example, you might want to return null
if there is an error in the processing, or another outcome value if the processing was successful. A return value of null
causes the navigation handler to forgo evaluating navigation cases and to immediately redisplay the current page.
Tip:
To trigger a specific navigation case, the outcome value returned by the method must exactly match the outcome value in the navigation rule in a case-sensitive way.The command button is now bound to this new method using the Action
attribute instead of the ActionListener
attribute. If a value had previously existed for the Action
attribute (such as an outcome string), that value is added as the return for the new method. If there was no value, the return is kept as null
.
When you ovrerride a declarative method, JDeveloper adds a managed property to your backing bean with the managed property value of #{bindings}
(the reference to the binding container), and it adds a strongly-typed bean property to your class of the BindingContainer
type which the JSF runtime will then set with the value of the managed property expression #{bindings}
. JDeveloper also adds logic to the UI command action method. This logic includes the strongly-typed getBindings()
method used to access the current binding container.
Example 17-8 shows the code that JDeveloper adds to the chosen managed bean. Notice that the return String
"Complete
" was automatically added to the method, as that was the value of the action
attribute.
Example 17-8 Generated Code in a Backing Bean to Access the Binding Object
private BindingContainer bindings; ... public String createSRButton_action() { BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("createServiceRequest"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } return "Complete"; }
This code does the following:
Finds the binding for the associated method, and executes it
Adds a return for the method that can be used for navigation. By default the return is null
, or if an outcome string had previously existed for the button's Action
attribute, that attribute is used as the return value. You can change this code as needed. For more information about using return outcomes, see Section 16.4, "Using Dynamic Navigation".
JDeveloper automatically rebinds the UI command component to the new method using the Action
attribute, instead of the ActionListener
attribute. For example, Example 17-9 shows the code on a JSF page for a command button created by dropping the createServiceRequest
method. Notice that the actionListener
attribute is bound to the createServiceRequest
method, and the action
attribute has a String
outcome of Complete
. If the user were to click the button, the method would simply execute, and navigate to the page defined as the toViewId
for this navigation case.
Example 17-9 JSF Page Code for a Command Button Bound to a Declarative Method
<af:commandButton actionListener="#{bindings.createServiceRequest.execute}" text="createServiceRequest" disabled="#{!bindings.createServiceRequest.enabled}" id="createSRButton" action="Complete"/>
Example 17-10 shows the code after overriding the method on the page's backing bean. Note that the action
attribute is now bound to the backing bean's method.
Example 17-10 JSF Page Code for a Command Button Bound to an Overridden Method
<af:commandButton text="createServiceRequest" disabled="#{!bindings.createServiceRequest.enabled}" id="createSRButton" action="#{backing_SRCreateConfirm.createSRButton_action}"/>
Tip:
If when you click the button that uses the overridden method, you receive this error:SEVERE: Managed bean main_bean could not be created The scope of the referenced object: '#{bindings}' is shorter than the referring object
it is because the managed bean that contains the overriding method has a scope that is greater than request
, (that is, either session
or application
). Because the data in the binding container referenced in the method has a scope of request
, the scope of this managed bean must be set to request
scope or a lesser scope.