| Oracle® Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework 11g Release 1 (11.1.1) Part Number B31974-02 |
|
|
View PDF |
This chapter describes how to add more complex bindings to your pages. It describes how to use methods that take parameters for creating forms and command components. It also includes information about creating contextual events and using ADF model-level validation.
This chapter includes the following sections:
Section 26.2, "Creating Command Components to Execute Methods"
Section 26.3, "Setting Parameter Values Using a Command Component"
Note:
Some of the implementation methods in this chapter are intended for page-level designs. If you are using task flows, you may be able to perform many of the same functions. For more information, see Chapter 13, "Getting Started with ADF Task Flows".Once you create a basic page and add navigation capabilities, you may want to add more complex features, such as passing parameters between pages or providing the ability to override declarative actions. Oracle ADF provides many features that allow you to add this complex functionality using very little actual code.
Some of the functions described in this chapter may be performed using other methodology. For example, if you are using task flows instead of individual page flows, you should use the task flow parameter passing mechanism. Or, if you are using Business Components, you should use the Business Components validation rules instead of ADF Model validation rules. For more information about validation rules for Business Components, see Chapter 7, "Defining Validation and Business Rules Declaratively".
When your application contains custom methods, these methods appear in the Data Controls panel. 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 creating custom methods, see Section 9.7, "Customizing an Application Module with Service Methods" and Section 9.8, "Publishing Custom Service Methods to UI Clients".
Note:
If you are using task flows, you can call methods directly from the task flow definition. For more information, see Section 14.5, "Using Method Call Activities".For example, the application module in the StoreFront module of the Fusion Order Demo application contains the updateShoppingCartItem(Integer Integer) method. This method updates the items in the shopping cart. To allow the user to execute this method, you drag the updateShoppingCartItem(Integer, Integer) method from the Data Controls panel, as shown in Figure 26-1.
In order to perform the required business logic, many methods require a value for their parameter or parameters. This means that when you create a button bound to the method, you need to specify where the value for the parameter(s) is to be retrieved from.
For example, if you use the updateShoppingCartItem(Integer Integer) method, you need to specify the items to be updated.
To add a button bound to a method:
From the Data Controls panel, 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 Create > Methods > ADF Button.
If the method takes parameters, the Edit Action Binding dialog opens. In the Edit Action Binding dialog, enter values for each parameter or click the Show EL Expression Builder menu selection in the Value column of Parameters to launch the EL Expression Builder.
When you drop a method as a command button, JDeveloper:
Defines a method action binding for the method. Action bindings use the RequiresUpdateModel property, which determines whether or not the model needs to be updated before the action is executed. For command operations, this property is set to true by default, which means that any changes made at the view layer must be moved to the model before the operation is executed.
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 20.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 updateShoppingCartItem(Integer, Integer) method action binding contains NamedData elements for the parameter. This element is bound to the value specified when you created the action binding. Example 26-1 shows the method action binding created when you drop the updateShoppingCartItem(Integer Integer) method, and bind the Integer parameter (named productId) and the other Integer parameter (named quantity) to the appropriate variables.
Example 26-1 Method Action Binding for a Parameter Method
<methodAction id="updateShoppingCartItem" RequiresUpdateModel="true"
Action="invokeMethod" MethodName="updateShoppingCartItem"
IsViewObjectMethod="false"
DataControl="StoreServiceAMDataControl"
InstanceName="StoreServiceAMDataControl.dataProvider">
<NamedData NDName="productId" NDValue="4" NDType="java.lang.Integer"/>
<NamedData NDName="quantity" NDValue="5" NDType="java.lang.Integer"/>
</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 using an EL expression. This EL expression causes the binding's method to be invoked on the application module. For more information about the command button's actionListener attribute, see Section 20.4.3, "What Happens at Runtime: How Action Events and Action Listeners Work".
Tip:
Instead of binding a button to theexecute method on the action binding, you can bind the button to the 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 26.4, "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 26-2 shows the EL expression used to bind the command button to the updateShoppingCartItem(Integer, Integer) method.
Example 26-2 JSF Code to Bind a Command Button to a Method
<af:commandButton actionListener="#{bindings.updateShoppingCartItem.execute}"
text="updateShoppingCartItem"
disabled="#{!bindings.updateShoppingCartItem.enabled}"/>
Tip:
When you drop a command button component onto the page, JDeveloper automatically gives it an ID based on the number of the same type of component that was previously dropped. For example,commandButton1, commandButton2. If you change the ID to something more descriptive, you must manually update any references to it in any EL expressions in the page.You can also use the return value from a method call. Example 26-3 shows a custom method that returns a string value.
Example 26-3 Custom Method That Returns a Value
/**
* Custom method.
*/
public String getHelloString() {
return ("Hello World");
}
Example 26-4 shows the code in the JSPX page for the command button and an outputText component.
Example 26-4 Command Button to Call the Custom Method
<af:commandButton actionListener="#{bindings.getHelloString.execute}"
text="getHelloString"
disabled="#{!bindings.getHelloString.enabled}"
id="helloButtonId"/>
<af:outputText value="#{bindings.return.inputValue}"
id="helloOutputId"/>
When the user clicks the command button, it calls the custom method. The method returns the string "Hello World" to be shown as the value of the outputText component.
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 updateShoppingCartItem(Integer, Integer) method, the method takes the values of the product Id and quantity and updates the shopping cart.
There may be cases where an action on one page needs to set parameters that will be used to determine application functionality. For example, you can create a search command button on one page that will navigate to a results table on another page. But the results table will display only if a parameter value is false.
You can use a managed bean to pass this parameter between the pages, and to contain the method that is used to check the value of this parameter. The managed bean is instantiated as the search page is rendered, and a 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.
For more information about creating custom methods, see Section 9.7, "Customizing an Application Module with Service Methods" and Section 9.8, "Publishing Custom Service Methods to UI Clients".
Note:
If you are using task flows, you can use the task flow parameter passing mechanism. For more information, see Chapter 15, "Using Parameters in Task Flows".A setPropertyListener component with type property set to action, which is nested in the command button that executed this search, is then used to set this flag to false, thus causing the results table to display once the search is executed. For information about using managed beans, see Section 18.4, "Using a Managed Bean in a Fusion Web Application".
You can use the setPropertyListener component to set values on other objects. This component must be a child of a command component.
Before you add the setPropertyListener to the command component, you must have already created a command component on the page.
To use the setPropertyListener component:
From the Component Palette, drag a setPropertyListener component and drop it as a child of the command component.
Or right-click the component and select Insert inside Button > ADF Faces > setPropertyListener.
In the Insert Set Property Listener 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 18.4, "Using a Managed Bean in a Fusion Web Application". Additionally, the data in a binding container is valid only during the request in which the container was prepared. The data may change between the time you set it and the time the next page is renderedSelect Action from the Type dropdown menu.
The setPropertyListener component lets the command component set a value before it navigates to the next page. When you set the From attribute either to the source of the value you need to pass or to 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 26-5 shows the code on the JSF page for a command component that takes the value false and sets it as the value of the initialSearch flag on the searchResults managed bean.
When a user clicks the command component, before navigation occurs, the setPropertyListener component sets the parameter value. In Example 26-5, the setPropertyListener takes the value false and sets it as the value for the initialSearchattribute on the searchResults 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 #{searchResults.initialSearch}.
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 existing logic.
Note:
If you are using task flows, you can call custom methods from the task flow. For more information, see Chapter 13, "Getting Started with ADF Task Flows".JDeveloper allows you to add logic to a declarative operation by creating a new method and property on a managed bean that provides 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 to the execute property on the original operation or method. Now when the user clicks the button, the new method is executed.
For example, in the Fusion Order Demo application Orders page, the Commit operation requires additional processing. The Commit button is renamed to Submit Orders and logic is added to the submitOrders method in the orderPageBean 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 override the declarative method if the command component currently has an EL expression as its value for theAction attribute, because 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.
The component is created and bound to the associated binding object in the ADF Model layer with the ActionListener attribute.
For more information about creating command components using methods on the Data Controls panel, see Section 26.2, "Creating Command Components to Execute Methods".
For more information about creating command components from operations, see Section 20.4.2, "What Happens When You Create Command Buttons"
On the JSF page, double-click 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 26-2.
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 26-3.
Click New to create a new backing bean. In the Create Managed Bean dialog, 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:
Whenever there is a value for theActionListener attribute on the command component, JDeveloper understands that the button is bound to the execute property of a binding. If you have removed that binding, you will not be given the choice to generate the ADF binding code. You will need to insert the code manually, or to set a dummy value for the ActionListener before double-clicking 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 26-6 shows the code inserted into the bean. In this example, a command button is bound to the Commit operation.
Example 26-6 Generated Code in a Backing Bean to Access the Binding Object
public String submitOrder() {
BindingContainer bindings = getBindings();
OperationBinding operationBinding =
bindings.getOperationBinding("Commit");
Object result = operationBinding.execute();
if (!operationBinding.getErrors().isEmpty()) {
return null;
}
}
You can now add logic either before or after the binding object is accessed, as shown in Example 26-7.
Example 26-7 Code Added to the Overridden Method
public String submitOrder() {
DCBindingContainer bindings =
(DCBindingContainer)JSFUtils.resolveExpression("#{bindings}");
OperationBinding operationBinding =
bindings.getOperationBinding("Commit");
JUCtrlAttrsBinding statusCode =
(JUCtrlAttrsBinding)bindings.findNamedObject("OrderStatusCode");
statusCode.setAttribute("OrderStatusCode", "PENDING");
JUCtrlAttrsBinding orderDate =
(JUCtrlAttrsBinding)bindings.findNamedObject("OrderDate");
orderDate.setAttribute("OrderDate", new Date());
JUCtrlAttrsBinding orderId =
(JUCtrlAttrsBinding)bindings.findNamedObject("OrderId");
System.out.println("OrderId = " + orderId);
JSFUtils.storeOnSession("orderId", orderId.getAttribute("OrderId"));
Object result = operationBinding.execute();
if (!operationBinding.getErrors().isEmpty()) {
return null;
}
In addition to any processing logic, you may also want to write conditional logic to return one of multiple outcomes. 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, including case.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 override 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.
The 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. 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.
JDeveloper automatically rebinds the UI command component to the new method using the Action attribute, instead of the ActionListener attribute. Example 26-8 shows the code when a Commit operation is declaratively added to a page.
Example 26-8 JSF Page Code for a Command Button Bound to a Declarative Method
<af:commandButton actionListener="#{bindings.Commit.execute}"
text="Commit"
disabled="#{!bindings.Commit.enabled}"/>
Example 26-9 shows the code after the method on the page's backing bean is overridden. Note that the action attribute is now bound to the backing bean's method.
Example 26-9 JSF Page Code for a Command Button Bound to an Overridden Method
<af:commandButton text="Submit Order"
action="#{orderPageBean.submitOrder}"/>
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 the same or a lesser scope.
Often a page or a region within a page needs information from somewhere else on the page or from a different region. While you can pass parameters to obtain that information, doing so makes sense only when the parameters are well known and the inputs are EL-accessible to the page. Parameters are also useful when a task flow may need to be restarted if the parameter value changes.
However, say you have a task flow with multiple page fragments that contain various interesting values that could be used as input on one of the pages in the flow. If you were to use parameters to pass the value, the task flow would need to surface output parameters for the union of each of the interesting values on each and every fragment. Instead, for each fragment that contains the needed information, you can define a contextual event that will be raised when the page is submitted. The page or fragment that requires the information can then subscribe to the various events and receive the information through the event.
For example, in the StoreFront module, a contextual event is used on the home page to determine the products to display on the product table based on the category selected in the category tree. When a user selects a category (for example, Electronics), the categoriesTaskFlow1 region that contains the tree raises an event, taking the selected category as its payload. An event map created in the home page's page definition file maps that event to the consumer, which is the region that contains the products table. Using the payload, a method binding on the product table region's page definition executes to determine the correct products to display based on the selected category.
Events are configured in the page definition file for the page or region that will raise the event (the producer). In order to associate the producer with the consumer that will do something based on the event, you create an event map also in the page definition (when using events between regions, the page definition file for the page in the region that consumes the event contains the event map). Even if the consuming page is in a dynamic region, the event map should be in the page definition file of the consuming page.
You can raise a contextual event for an action binding, a method action binding, a value attribute binding, a tree binding, a table binding, or a list binding. For action and method action bindings, the event is raised when the action or method is executed. For a value attribute binding, the event is triggered by the binding container and raised after the attribute is set successfully. For a range binding (tree, table, list), the event is raised after the currency change has succeeded. Value attribute binding and range binding contextual events may also be triggered by navigational changes.
Contextual events are not the same as events raised by UI components. For a description of these types of events, see the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework. Contextual events can be used in association with UI events. In this case, an action listener that is invoked due to a UI event can, in turn, invoke a method action binding that then raises the event.
You create contextual events by first creating the event on the producer. You then determine the consumer of the event and map the producer and consumer.
Before you create a contextual event, you must have a method binding, action binding, value attribute binding, or list binding on the page. If you do not, you must create the binding first. For example, for a method binding, in the Structure window, right-click the binding and choose Insert inside bindings > Generic Bindings > methodAction and add the method action binding. For the other bindings, you may need to drop a component such as an input text, table, or tree to the page.
To create a contextual event:
Open the page definition file that contains the binding for the producer of the event.
A producer must have an associated binding that will be used to raise the event. For example, if a method or operation will be the producer, the associated action binding or method action binding will contain the event.
If you do not have a method binding, action binding, value attribute binding, or list binding on the page, you need to create the binding first. For example, for a method binding, in the Structure window, right-click the binding and choose Insert inside bindings > Generic Bindings > methodAction and add the method action binding. For the other bindings, you may need to drop a component such as an input text, table, or tree to the page.
In the Structure window, right-click the binding for the producer and choose Insert inside binding name > events or Insert inside binding name > Contextual Events > events.
In the Structure window, right-click the events element just created, and choose Insert inside events > event.
In the Insert event dialog, enter a name for the event in the name field, and click Finish.
The event is now created. By default, any return of the associated method or operation will be taken as the payload for the event and stored in the EL-accessible variable ${payLoad}. You now need to map the event to the consumer, and to configure any payload that needs to be passed to the consumer.
Open the page definition that contains the binding for the consumer.
The binding container represented by this page provides access to the events from the current scope, including all contained binding containers (such as task flow regions). If regions or other nested containers need to be aware of the event, the event map should be in the page definition of the page in the consuming region.
In the Structure window, right-click the topmost node that represents the page definition, and choose Edit Event Map.
Note:
If the producer event comes from a page in an embedded dynamic region, you may not be able to edit the event map using the Event Map editor. You can manually create the event map by editing the page definition file or use insert inside steps as described in Section 26.5.2, "How to Manually Create the Event Map".In the Edit Event Map dialog, do the following:
Use the Producer dropdown menu to choose the producer.
Use the Event Name dropdown menu to choose the event.
Use the Consumer dropdown menu to choose the consumer. This should be the actual method that will consume the event.
If the consuming method or operation requires parameters, click the Parameters ellipses button. In the Edit Consumer Params dialog, in the Param Name field, enter the name of the parameter expected by the method. In the Param Value field, enter the value. If this is to be the payload from the event, you can access this value using the ${payLoad} expression. If the payload contains many parameters and you don't need them all, use the ellipses button to open the Expression Builder dialog. You can use this dialog to select specific parameters under the payload node.
In the Event Map Editor dialog, click Add New to add the event map to the event maps in the page definition.
Click OK to add the map to the page definition file.
Under most circumstances, you can create the event map using the Event Map Editor as described in Section 26.5.1, "How to Create Contextual Events". However, in situations such as when the producer event is from a page in an embedded dynamic region, the Event Map Editor at design time cannot obtain the necessary information to create an event map.
To create the event map manually:
Open the page definition that contains the binding for the consumer.
In the Structure window, right-click the topmost node that represents the page definition, and choose Insert inside pagedef name > eventMap.
An eventMap node appears in the Structure pane.
Select the eventMap node, right-click and choose Insert inside eventMap > event.
In the Insert Event dialog, enter the name of the event and click OK.
An event node appears under the eventMap node.
Repeat this step to add more events.
Select event, right-click and choose Insert inside event > producer.
The Insert producer dialog appears. Enter the name of the binding that is producing this event. You can also enter the name of the producer region, in which case all the consumers specified under this tag can consume the event. You can also enter "*" to denote that this event is available for all consumers under this tag. Click OK.
A producer node appears under the event node.
Select producer, right-click and choose Insert inside producer > consumer.
The Insert consumer dialog appears. Enter the name of the handler that will consume the event. Click OK.
A consumer node appears under the producer node.
Repeat this step to add more consumers.
If there are parameters being passed, add the parameter name and value. Select consumer, right-click and choose Insert inside consumer > parameters.
A parameters node appears under the consumer node.
Select parameters, right-click and choose Insert inside parameters > parameter.
The Insert parameter dialog appears. Enter the name of the parameter and the value of the parameter. The value can be an EL expression. Click OK.
Repeat Insert inside parameters > parameter to add more parameters
When you create an event for the producer, JDeveloper adds an events element to the page definition file. Each event name is added as a child. Example 26-10 shows the event on the setSelectedProductCatagory method action binding in the categories_categoriesTreePageDef page definition file of the StoreFront module. This is the page definition for the region used to display the category tree.
Example 26-10 Event Definition for the Producer
<methodAction id="setSelectedProductCategory"
InstanceName="CategoriesContextHandler.dataProvider"
DataControl="CategoriesContextHandler"
RequiresUpdateModel="true" Action="invokeMethod"
MethodName="setSelectedProductCategory"
IsViewObjectMethod="false"
ReturnName="CategoriesContextHandler.methodResults.
CategoriesContextHandler_dataProvider_setSelectedProductCategory_result">
<NamedData NDName="categoryName" NDType="java.lang.String"/>
<NamedData NDName="categoryId" NDType="oracle.jbo.domain.Number"/>
<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="selectCategory"/>
</events>
When the action binding is invoked, the event is created and the return of the method is added to the payload of the event.
When you configure an event map, JDeveloper creates an event map entry in the corresponding page definition file. Example 26-11 shows the event map on the homePageDef page definition file that maps the selectCategory event from the categoriestaskflow1 region to both the MostPopularProductsByCategoriesExecuteWithParams and the ProductsByCategoriesExecuteWithParams method bindings on the products_productsTablePageDef page definition file. Both of these consumers invoke methods that determine the products to display based on the parameters that are passed into it. The mapping is in the homePageDef page definition, as that is the parent container for both the categoriestaskflow1 and the MostPopularProductsFlow regions.
Example 26-11 Event Map in Parent Page Definition File
<eventMap>
<event name="selectCategory">
<producer region="categoriestaskflow1">
<consumer handler="products_productsTablePageDef.
MostPopularProductsByCategoriesExecuteWithParams"
region="MostPopularProductsFlow">
<parameters>
<parameter name="category" value="${PayLoad['CategoryId']}"/>
</parameters>
</consumer>
<consumer handler="products_productsTablePageDef.
ProductsByCategoriesExecuteWithParams"
region="MostPopularProductsFlow">
<parameters>
<parameter name="category" value="${PayLoad['CategoryId']}"/>
</parameters>
</consumer>
</producer>
</event>
</eventMap>
When both the event producer and the consumer are defined in the same page definition file, as the corresponding page is invoked and the binding container is created, the event is raised when the corresponding method or action binding is executed, when a value binding is set successfully, or when a range binding currency is set successfully.
For a method binding, the result of the method execution forms the payload of the event, and the payload is queued. The method binding passes the event to the binding container for dispatch. The event dispatcher associated with the binding container checks the event map (also in the binding container, as it is part of the same page definition file) for a consumer interested in that event and delivers the event to the consumer at the end of the lifecycle. The payload is then removed from the queue.
When the producer and consumer are in different regions, the event is first dispatched to any consumer in the same container, and then the event propagation is delegated to the parent binding container. This process continues until the parent or the topmost binding container is reached. After the topmost binding container is reached, the event is again dispatched to child binding containers that have regions with pages having producer set to wildcard "* ".
In the model layer, ADF Model validation rules can be set for a binding's attribute on a particular page. When a user edits or enters data in a field and submits the form, the bound data is validated against any set rules and conditions. If validation fails, the application displays an error message.
Note that you don't need to add additional ADF Model validation if you have already set validation rules in the business domain layer of your entity objects. In a Business Components-based Fusion web application, unless you use data controls other than your application module data controls, you won't need to use ADF Model validation.
You set ADF Model validation on the page definition file. You define the validation rule, and set an error message to display when the rule is broken.
Table 26-1 describes the ADF Model validation rules that you can configure for a binding's attributes.
Table 26-1 ADF Model Validation Rules
| Validator Rule Name | Description |
|---|---|
|
Compare |
Compares the attribute's value with a literal value |
|
List |
Validates whether or not the value is in or is not in a list of values |
|
Range |
Validates whether or not the value is within a range of values |
|
Length |
Validates the value's character or byte size against a size and operand (such as greater than or equal to) |
|
Regular Expression |
Validates the data using Java regular expression syntax |
|
Required |
Validates whether or not a value exists for the attribute |
To create an ADF Model validation rule:
Open the page definition that contains the binding for which you want to create a rule.
In the Structure window, select the attribute, list, or table binding.
In the Property Inspector, select More and then Edit Validation Rule.
In the Edit Validation Rules dialog, expand the binding node, select the attribute name, and click New.
In the Add Validation Rule dialog, select a validation rule and configure the rule accordingly.
Select the Failure Handling tab and configure the message to display when the rule is broken.
When a user submits data, as long as a submitted value is a non-null value or a string value of at least one character, then all validators on a component are called one at a time. Because the f:validator tag on the component is bound to the validator property on the binding, any validation routines set on the model are accessed and executed.
The process then continues to the next component. If all validations are successful, the Update Model Values phase starts and a local value is used to update the model. If any validation fails, the current page is redisplayed along with the error message.
When you use the Data Controls panel to create input components, JDeveloper inserts the af:messages tag at the top of the page. This tag can display all error messages in the queue for any validation that occurs on the server side, in a box offset by color. If you choose to turn off client-side validation for ADF Faces, those error messages are displayed along with any ADF Model error messages. ADF Model messages are shown first. Messages are shown within the af:messages tag, and with the associated components.
Figure 26-4 shows the error message for an ADF Model validation rule, which states that the value the user entered is not acceptable.
You can display server-side error messages in a box at the top of a page using the af:messages tag. When you drop any item from the Data Controls panel onto a page as an input component, JDeveloper automatically adds this tag for you.
To display error messages in an error box:
In the Structure window, select the af:messages tag.
This tag is created automatically whenever you drop an input widget from the Data Controls panel. However, if you need to insert the tag manually, simply add the code, as shown in Example 26-12, within the af:document tag.
In the Property Inspector set the following attributes:
globalOnly: By default, ADF Faces displays global messages (that is, messages that are not associated with components), followed by individual component messages. If you wish to display only global messages in the box, set this attribute to true. Component messages will continue to display with the associated component.
Inline: Specify whether to render the message list inline with the page or in a popup window.
message: The main message text that displays just below the message box title, above the list of individual messages.
Ensure that client-side validation has been disabled. If you do not disable client-side validation, the alert dialog will display whenever there are any ADF Faces validation errors and prevent propagation of the error to the server.
To disable client-side validation, add an entry for <client-validation-disable> and set it to true in the trinidad-config.xml file, as shown in Example 26-13.
Example 26-13 Disabling Client-Side Validation in the Trinidad-config.xml
<?xml version="1.0" encoding="windows-1252"?> <trinidad-config xmlns="http://myfaces.apache.org/trinidad/config"> <skin-family>blafplus-rich</skin-family> <client-validation-disabled>true</client-validation-disabled> </trinidad-config>
You can report errors using a custom error handler that extends the default DCErrorHandlerImpl class. You are not required to write any code to register your custom exception handler class. Instead, you select the root node of the DataBindings.cpx file in the Structure window, and then use the Property Inspector to set the ErrorHandlerClass property to the fully qualified name of the error handler you want it to use.
Your custom error handler can contain the following overridable methods:
reportException(): Called to report any exception that occurs. It can be overridden to analyze reported exceptions.
getDisplayMessage(): Returns the message that will be reported to JSF for each error that occurs. Returning null is the way your custom error handler signals that a given exception should not be reported to the client.
processMessage(): Called every time an exception is transformed into a Faces message. It provides the ability to change the content of the message that will be displayed.
Example 26-14 illustrates a custom error handler that extends the DCErrorHandlerImpl class.
Example 26-14 Custom Error Handler
package view.controller.fwkext;
import java.util.ArrayList;
import java.util.List;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCErrorHandlerImpl;
import oracle.jbo.CSMessageBundle;
import oracle.jbo.DMLConstraintException;
import oracle.jbo.JboException;
public class CustomErrorHandler extends DCErrorHandlerImpl {
List<ExceptionMapper> exceptionMapperList = new
ArrayList<ExceptionMapper>();
public CustomErrorHandler() {
this(true);
}
public CustomErrorHandler(boolean setToThrow) {
super(setToThrow);
exceptionMapperList.add(new DisableJboExceptionCodesMapper());
}
public void reportException(DCBindingContainer bc, Exception ex) {
for (ExceptionMapper mapper : exceptionMapperList) {
if (mapper.canMapException(ex)) {
ex = mapper.mapException(ex);
}
}
super.reportException(bc, ex);
}
You must change the constructor to MyErrorHandler(). The exception error handler must have a default constructor, as shown in Example 26-15.
Example 26-15 Default Constructor
ErrorHandlerClass="viewcontroller.MyErrorHandler" public MyErrorHandler() { super(true); }
Oracle ADF constructs an instance of the custom error handler for each BindingContext object that is created. Because Oracle ADF serializes simultaneous web requests from the same logical end-user session, multiple threads generally will not use the same error handler at the same time. However, to guarantee a thread-safe custom error handler, use the setProperty() API on JboException. This method stores in the exception objects themselves any hints you might need later during the phase when exceptions are translated to JSF FacesMessage objects for display.