There is a great video from Frank Nimphius - How-to use Contextual Events in ADF Region Interaction. Frank explains how you can trigger refresh and pass data between two independent regions using ADF Contextual Framework. While this works great, sometimes you may need something more simpler. I decided to explain another option - how to trigger refresh between two independent regions using ADF Task Flow parameters. Its just another option, you should use ADF Contextual Events Framework in most of the cases, but it always good to know alternatives. Described solution will work for the regions based on the shared Data Control.
Download sample application - RegionCommunicationPPR.zip. This sample is based on two ADF Regions - one brings Employees table data, another - Employes form data. User can select data in the table, form in the second ADF Region will be refreshed automatically and will show synchronized data.
Two ADF Regions:
How we can trigger second ADF Region refresh based on the changes from the first ADF Region? We will use ADF Task Flow parameters for the second ADF Region:
Its enough just to define parameter, no need to use - it is needed only to trigger refresh, the rest will be done automatically, because both regions are based on the same Data Control.
Next, we go to Page Definition file where both ADF Regions are declared:
Edit second ADF Region - employeesformflow1, its where we have declared Input Parameter. Make sure that Input Parameter value is not empty:
Here is a main trick of this post - Input Parameter of the second ADF Region should get value from EmployeeId attribute in the first ADF Region:
And second point - Refresh condition for the second ADF Region must be set to ifNeeded. This will make sure, that each time when input parameter value will be changed, it will trigger refresh for the second ADF Region and synchronize data:
Table selection from the first ADF Region triggers refresh in the second and shows the same row:
If you select another row - both table and form are synchronized again. Change Salary attribute value from 9000 to 10000:
Press Save button - changes will be stored and both ADF Regions are synchronized:
Sunday, April 25, 2010
Communicating Between ADF Regions Without Contextual Events Framework
Labels:
ADF,
ADF Task Flow,
JDeveloper 11g
Saturday, April 24, 2010
ADF Magic or Bad Database Design for Master-Detail - Editable Primary Keys
While helping Oracle Forms customers to modernize their systems into Oracle Fusion, sometimes we face interesting requirements. Together with my colleagues from Cape Town (South Africa) we are implementing Oracle Fusion system based on database design inherited from old Oracle Forms system. One of the most common use cases there - Master-Detail relationships across different levels. Some of the Master-Detail use cases are using editable primary keys. This means there is no database sequence for primary key, user types key value directly from interface. And its where problem starts - because we are using incorrect design and asking user to provide primary key values manually, things are not working as expected. But what to do, sometimes we can't change design and are forced to find solutions. Today I will describe what type of problems you will face with this use case, and how to solve it with a bit of ADF magic. Its where ADF magic comes - even we are using wrong database design, we still can make it work with ADF.
Download working sample application - CreateMasterDetailNoSequence.zip. This sample demonstrates how you can insert new records into Master and Detail entities, without using auto generated sequence numbers. I want to stress again - its not recommended, but we can't change DB design :-) There is no DB Sequence, and no Triggers - user types primary key value manually.
First we create new Master record - new Region:
New blank record is inserted into Regions table, with editable primary key - RegionId. Let's type key value - 99 and provide RegionName as well:
Without saving Master record, insert new record into Detail table - Countries:
You can see that foreign key - RegionId, is not populated for Countries. This means we can't save Master-Detail data, because integrity is broken. If we would use auto generated database sequence values, it would work perfectly. But now, we need to apply ADF magic - let's move Create Country button from Detail block into Master block.
I assume, when create button for Detail is above Master, it somehow submits entered Master key value, before actually triggering Detail block. Its why it works now - I press Create Country button and it successfully inserts new record with foreign key populated:
Both - newly inserted Master and Detail can be commited to database together:
Just make sure that Create button for Detail entity is set as Partial Trigger for Detail Panel Collection component:
I highly recommend to use auto generated values for primary keys, always avoid editable primary keys. If there is possibility, better to define surrogate keys.
Described behavior happens with Master-Details implemented in ADF Fragments. It doesn't have this side effect, when using simple pages.
Download working sample application - CreateMasterDetailNoSequence.zip. This sample demonstrates how you can insert new records into Master and Detail entities, without using auto generated sequence numbers. I want to stress again - its not recommended, but we can't change DB design :-) There is no DB Sequence, and no Triggers - user types primary key value manually.
First we create new Master record - new Region:
New blank record is inserted into Regions table, with editable primary key - RegionId. Let's type key value - 99 and provide RegionName as well:
Without saving Master record, insert new record into Detail table - Countries:
You can see that foreign key - RegionId, is not populated for Countries. This means we can't save Master-Detail data, because integrity is broken. If we would use auto generated database sequence values, it would work perfectly. But now, we need to apply ADF magic - let's move Create Country button from Detail block into Master block.
I assume, when create button for Detail is above Master, it somehow submits entered Master key value, before actually triggering Detail block. Its why it works now - I press Create Country button and it successfully inserts new record with foreign key populated:
Both - newly inserted Master and Detail can be commited to database together:
Just make sure that Create button for Detail entity is set as Partial Trigger for Detail Panel Collection component:
I highly recommend to use auto generated values for primary keys, always avoid editable primary keys. If there is possibility, better to define surrogate keys.
Described behavior happens with Master-Details implemented in ADF Fragments. It doesn't have this side effect, when using simple pages.
Labels:
ADF,
JDeveloper 11g
Wednesday, April 21, 2010
Overview: Developing Large Oracle ADF 11g Applications on ODTUG Kaleidoscope 2010
I have prepared my presentation for ODTUG Kaleidoscope 2010 conference - Developing Large Oracle ADF 11g Applications. I will talk about ADF 11g architecture development and this session will have only few PPT slides, most of the time I will demo and explain ADF code - expect some hardcore ADF topics :-)
Main focus will be stressed on:
See you in Washington DC, June 2010.
Update 9/7/2010
Download presentation slides - ABaranovskis_ODTUG10.pdf, download sample applications - ADF Integration.
Main focus will be stressed on:
See you in Washington DC, June 2010.
Update 9/7/2010
Download presentation slides - ABaranovskis_ODTUG10.pdf, download sample applications - ADF Integration.
Labels:
Events,
ODTUG Kaleidoscope
Sunday, April 18, 2010
Dynamic Iterator Binding to Reuse View Link Relationship for Master-Detail
Its very frequent requirement to implement Master-Detail relationships. Additionally it happens to render detail or master data for the same table, based on user selection. For example, we can have master screen with Locations data and another screen with detail data for Departments. But, if user opens Departments screen directly, it should bring complete list for Departments. So, complexity here - how to implement detail table for Departments data, with option to bring complete list of Departments as well.
This requirement can be solved mainly in three ways:
1. Duplicating Departments fragment - one for master and another for detail data
2. Implementing View Criteria to filter Departments conditionally
3. Implementing dynamic iterator binding and reusing View Link relationship
First option is highly not recommended, because it increasing number of fragments or pages - this means duplicating number of bugs. Second option is good and simple, however you need to declare View Criteria and filter View Object based on parameter. It works well, however we are not using built in ADF support for Master-Detail relationships. Third option - dynamic iterator binding works the best, because there is no need for View Criteria and we are reusing View Link functionality given by ADF.
Download sample application - DynamicIterator.zip. This sample implements a use case, where Departments data is rendered based on dynamic iterator declared in Page Definition.
As you can see from Data Control, we have two instances for Departments VO - DepartmentsMasterView and DepartmentsDetailView:
Controller layer implements Router activity, to open locations or departments fragments based on external parameter. If departments fragment will be opened directly, it should bring all departments:
In this sample, I'm mainly focusing on departments fragment and its Page Definition. Iterator for departments data:
Is binded to VO instance dynamically, using Expression Language. Based on page flow scope parameter, we set to use Master or Detail VO instance. Iterator binding is set dynamically:
Master and Detail instances for Departments VO:
Page flow scope parameter is set from Locations fragment:
Its value is initialized with Departments detail instance name - DepartmentsDetailView:
We can test it now, lets run our region with departments external parameter value, this will trigger conditional navigation in Router activity and will map master VO instance for departments iterator:
All departments are rendered by departments fragment:
We can navigate to Locations and select one of the locations, for example 1700:
Departments data is filtered now automatically, through View Link relationship implemented between Locations and Departments. This means, iterator for departments was initialized based on DepartmentsDetailView instance:
This requirement can be solved mainly in three ways:
1. Duplicating Departments fragment - one for master and another for detail data
2. Implementing View Criteria to filter Departments conditionally
3. Implementing dynamic iterator binding and reusing View Link relationship
First option is highly not recommended, because it increasing number of fragments or pages - this means duplicating number of bugs. Second option is good and simple, however you need to declare View Criteria and filter View Object based on parameter. It works well, however we are not using built in ADF support for Master-Detail relationships. Third option - dynamic iterator binding works the best, because there is no need for View Criteria and we are reusing View Link functionality given by ADF.
Download sample application - DynamicIterator.zip. This sample implements a use case, where Departments data is rendered based on dynamic iterator declared in Page Definition.
As you can see from Data Control, we have two instances for Departments VO - DepartmentsMasterView and DepartmentsDetailView:
Controller layer implements Router activity, to open locations or departments fragments based on external parameter. If departments fragment will be opened directly, it should bring all departments:
In this sample, I'm mainly focusing on departments fragment and its Page Definition. Iterator for departments data:
Is binded to VO instance dynamically, using Expression Language. Based on page flow scope parameter, we set to use Master or Detail VO instance. Iterator binding is set dynamically:
Master and Detail instances for Departments VO:
Page flow scope parameter is set from Locations fragment:
Its value is initialized with Departments detail instance name - DepartmentsDetailView:
We can test it now, lets run our region with departments external parameter value, this will trigger conditional navigation in Router activity and will map master VO instance for departments iterator:
All departments are rendered by departments fragment:
We can navigate to Locations and select one of the locations, for example 1700:
Departments data is filtered now automatically, through View Link relationship implemented between Locations and Departments. This means, iterator for departments was initialized based on DepartmentsDetailView instance:
Labels:
ADF,
JDeveloper 11g
Wednesday, April 14, 2010
Implementing Custom Generic PL/SQL Validator in Oracle ADF
In Oracle ADF you are not constrained only by built in validation rules, its possible to add new ones. This allows to build custom generic validation rules and use them across project through standard JDeveloper wizard. Today I will explain how you can define such rules, and will use example where PL/SQL function will be invoked to check validation logic.
Download sample application - CustomGenericValidator.zip. This sample implements custom validation rule, where it invokes PL/SQL validation logic for Number type attribute. In PL/SQL function value is checked to be positive, if not - FALSE is returned and validation rule fails.
To define custom validation rule - open Project Properties for Model project and navigate to Registered Rules section under Business Components group. Here you will be able to define new validation rules for your project:
New rule is created in Java class, this class must be specified together with package name and rule display name:
Once new rule is defined in Model project properties, this rule will become available through JDeveloper Validation Rule wizard - custom SalaryValidation rule is defined for Salary attribute in Employees entity:
When custom rule is selected, it brings Rule Definition tab - nothing important here:
On the next tab - Validation Execution, as with built in validation rule, you can specify conditional execution expression:
In Failure Handling tab, failure message is defined to inform user about validation failure:
Now let's look into validator code - validate() method is invoked automatically by the framework. Using input parameter type - JboValidatorContext, I can access current transaction and call PL/SQL function. Based on the result, if it is negative - ValidationException is thrown and user is informed about failure:
PL/SQL function I'm using in this example is really simple - it accepts NUMBER type parameter and if its value is negative, it returns FALSE:
On runtime, validation is successful for positive salary value:
It brings failure message for negative salary value, based on result from PL/SQL function:
The same rule can be reused in other EO's, this means PL/SQL will be always invoked from one central place - custom validator class.
Download sample application - CustomGenericValidator.zip. This sample implements custom validation rule, where it invokes PL/SQL validation logic for Number type attribute. In PL/SQL function value is checked to be positive, if not - FALSE is returned and validation rule fails.
To define custom validation rule - open Project Properties for Model project and navigate to Registered Rules section under Business Components group. Here you will be able to define new validation rules for your project:
New rule is created in Java class, this class must be specified together with package name and rule display name:
Once new rule is defined in Model project properties, this rule will become available through JDeveloper Validation Rule wizard - custom SalaryValidation rule is defined for Salary attribute in Employees entity:
When custom rule is selected, it brings Rule Definition tab - nothing important here:
On the next tab - Validation Execution, as with built in validation rule, you can specify conditional execution expression:
In Failure Handling tab, failure message is defined to inform user about validation failure:
Now let's look into validator code - validate() method is invoked automatically by the framework. Using input parameter type - JboValidatorContext, I can access current transaction and call PL/SQL function. Based on the result, if it is negative - ValidationException is thrown and user is informed about failure:
PL/SQL function I'm using in this example is really simple - it accepts NUMBER type parameter and if its value is negative, it returns FALSE:
On runtime, validation is successful for positive salary value:
It brings failure message for negative salary value, based on result from PL/SQL function:
The same rule can be reused in other EO's, this means PL/SQL will be always invoked from one central place - custom validator class.
Labels:
ADF,
JDeveloper 11g
Tuesday, April 6, 2010
Changed Row Highlighting in Oracle ADF Table
User always wants to be informed about changes he did, this means we should keep him informed - moreover with ADF 11g it can be done very easily. Let's say user is changing one row and moving to another, how he will remember what rows were changed and review them once again before submitting to database. Logical answer - by highlighting changed rows. I will describe this use case and will provide sample application.
Download my sample - RowHighlight.zip. You can run this sample on your machine and see that row color will be changed after you will leave edited row and move to the next one.
I'm editing FI_MGR row:
Next I'm changing data for AC_ACCOUNT row, without committing previously done changes for FI_MGR:
You can see - FI_MGR row is highlighted, this informs user that he did changes in that row and those changes will be submitted to database when he will press Save button.
I navigate to the next record - AC_ACCOUNT row becomes highlighted:
After pressing Save button, all user changes are submitted to database and row highlighting is removed:
Now I will tell you, how you can implement described use case. Check my sample application, you will see there in Page Definition file, that I have defined getRowStatusColor method:
This method is calling custom method available in View Object Implementation class - to check current row status. I will need to iterate through range set of rendered rows, this means I will provide method parameter dynamically:
I'm calling this method from Backing Bean class, where I'm evaluating current row status and returning color setting for InlineStyle property of ADF column component:
This means I will highlight each cell individually.
Method in Backing Bean class is accessing rendered row, passing it to getRowStatusColor method and returning red color in case if rendered row is Modified:
Download my sample - RowHighlight.zip. You can run this sample on your machine and see that row color will be changed after you will leave edited row and move to the next one.
I'm editing FI_MGR row:
Next I'm changing data for AC_ACCOUNT row, without committing previously done changes for FI_MGR:
You can see - FI_MGR row is highlighted, this informs user that he did changes in that row and those changes will be submitted to database when he will press Save button.
I navigate to the next record - AC_ACCOUNT row becomes highlighted:
After pressing Save button, all user changes are submitted to database and row highlighting is removed:
Now I will tell you, how you can implement described use case. Check my sample application, you will see there in Page Definition file, that I have defined getRowStatusColor method:
This method is calling custom method available in View Object Implementation class - to check current row status. I will need to iterate through range set of rendered rows, this means I will provide method parameter dynamically:
I'm calling this method from Backing Bean class, where I'm evaluating current row status and returning color setting for InlineStyle property of ADF column component:
This means I will highlight each cell individually.
Method in Backing Bean class is accessing rendered row, passing it to getRowStatusColor method and returning red color in case if rendered row is Modified:
Labels:
ADF,
JDeveloper 11g
Sunday, April 4, 2010
Dynamic Row Selection for ADF Table Component
It is a common use case, when user inserts new row into table and we want to prevent him from inserting one more row, before currently inserted row is not stored into database. Some customers like to have such functionality. You can implement this requirement with ADF pretty easily, just need to specify expression language for RowSelection property. I did a bit more and implemented complete use case, I will describe it today.
Download sample application - DynamicSelection.zip. This sample implements conditional check for Create button - it makes it disabled, when current row is a new row. I'm using ADF table clickToEdit functionality, this allows to improve table data rendering perfomance:
The problem with default implementation - user can select any other record and Create button will become enabled again, even user still didn't saved new record into database:
We want to prevent this and make sure Create button remains always disabled, when there is new (not yet saved) record available. We can achieve this by substituting default RowSelection property value with expression language:
I will change single value to the following expression language statement:
You can see that I'm accessing custom method to get information about current row status (New, Unmodified, etc.) and if current row is not newly inserted row - selection will be enabled, otherwise it is disabled. Its a key thing - it will disable row selection, when there will be new row in the table (before it is saved).
Same custom method is used to disable/enable Create button itself:
Now, when there is new row - Create button remains disabled always and user cant select any other records, because table record selection is conditionally disabled:
When the record is saved - Create button becomes enabled and user can select records again:
Custom method to check if current row is a new row is coming from View Object implementation class:
Download sample application - DynamicSelection.zip. This sample implements conditional check for Create button - it makes it disabled, when current row is a new row. I'm using ADF table clickToEdit functionality, this allows to improve table data rendering perfomance:
The problem with default implementation - user can select any other record and Create button will become enabled again, even user still didn't saved new record into database:
We want to prevent this and make sure Create button remains always disabled, when there is new (not yet saved) record available. We can achieve this by substituting default RowSelection property value with expression language:
I will change single value to the following expression language statement:
You can see that I'm accessing custom method to get information about current row status (New, Unmodified, etc.) and if current row is not newly inserted row - selection will be enabled, otherwise it is disabled. Its a key thing - it will disable row selection, when there will be new row in the table (before it is saved).
Same custom method is used to disable/enable Create button itself:
Now, when there is new row - Create button remains disabled always and user cant select any other records, because table record selection is conditionally disabled:
When the record is saved - Create button becomes enabled and user can select records again:
Custom method to check if current row is a new row is coming from View Object implementation class:
Labels:
ADF,
JDeveloper 11g
Subscribe to:
Posts (Atom)