ADF Bindings and Data Control maps together ADF BC and ADF UI. Mapping by default is static, means ADF UI renders data collection from specific VO mapped during design time. Sometime we have requirement to change data collection rendered on ADF UI during runtime. Dynamic UI can be implemented with dynamic VO's in combination with ADF Faces Dynamic Tags (Shay Shmeltzer's blog -
ADF Faces Dynamic Tags - For a Form that Changes Dynamically). Dynamic VO's are created from SQL statements during runtime - such VO's will be readonly and hard to maintain. Moreover, its very hard to customize ADF Faces Dynamic Tags layout for custom UI requirements. We can set iterator binding in Page Definition dynamically, but this works only for Master-Detail VO instances - Dynamic Iterator Binding to Reuse View Link Relationship for Master-Detail. I will present in this blog post different technique how to implement dynamic editable ADF UI, assuming number of rendered attributes is not changing - but changing data collection.
Our task - to split ADF BC and ADF UI, allow to switch during runtime from one VO to another:
Sample application - DynamicVoApp.zip, implements such use case where we switch between different VO's retrieving data from different tables (we assume VO attribute names are the same, even underlying DB table column names are different). This is common scenario for business transactional systems, such systems tend to have large number of small tables with same columns and you need to minimize number of ADF UI to render such tables based on user role, etc. Of course we could build 100 different ADF UI screens for each of 100 tables - but we can be little smarter and build only 1 ADF UI screen to render all 100 different tables. Let's see how.
First we need to define dummy proxy VO, with select * from dual - we will use this proxy VO to access real VO through Data Control from ADF UI. DataView dummy VO will act as proxy VO:
Dummy VO - DataView defines all attributes to be accessible from ADF UI and to be defined in Page Definition file (Id and Name):
Real VO - DepartmentsView, must have exactly same names for attributes as we have defined in DataView. This is important, otherwise ADF Binding will fail to retrieve attributes from Page Definition on runtime - DepartmensView (Id attribute maps with Id, Name attribute maps with Name):
Same for JobsView, only difference - Name attribute maps with Title:
AM Data Model includes only DataView, only proxy dummy VO is accessible from ADF UI in our case:
There is custom method exposed in AM, this method will be invoked as Task Flow initializer and prepare real VO to be used through proxy DataView VO:
Here is main logic for sample application - changeVOInstance(voName) method:
This method logic:
1. Get proxy DataView VO
2. Remove proxy DataView VO definition
3. Find real VO definition by path name using ViewDefImpl.findDefObject(voName) function
4. Substitute proxy DataView VO with real VO definition, by creating real VO instance under proxy VO name using createViewObject("DataView", voDefImpl)
Method is invoked as initializer method from Task Flow:
Task Flow accepts VO path name parameter, it is passed as parameter to custom method:
Task Flow parameter is passed through PageFlow scope and Task Flow is set to be refreshed when parameter value changes (ifNeeded):
ADF UI is based on typical ADF UI component, no dynamic components - means highly customizable:
ADF UI bindings are pointing to Page Definition, where mapping with proxy DataView is defined:
On runtime, by default we render editable JobsView (Id and Title labels) - Jobs data:
Click Open Departments button - proxy DataView will be switched and DepartmentsView will be rendered instead (Id and Name labels) - Departments data:
This approach allows to edit and update rendered data:
As you can see, just with one ADF screen we have implemented two use cases - Jobs and Departments editable UI's.
Our task - to split ADF BC and ADF UI, allow to switch during runtime from one VO to another:
Sample application - DynamicVoApp.zip, implements such use case where we switch between different VO's retrieving data from different tables (we assume VO attribute names are the same, even underlying DB table column names are different). This is common scenario for business transactional systems, such systems tend to have large number of small tables with same columns and you need to minimize number of ADF UI to render such tables based on user role, etc. Of course we could build 100 different ADF UI screens for each of 100 tables - but we can be little smarter and build only 1 ADF UI screen to render all 100 different tables. Let's see how.
First we need to define dummy proxy VO, with select * from dual - we will use this proxy VO to access real VO through Data Control from ADF UI. DataView dummy VO will act as proxy VO:
Dummy VO - DataView defines all attributes to be accessible from ADF UI and to be defined in Page Definition file (Id and Name):
Real VO - DepartmentsView, must have exactly same names for attributes as we have defined in DataView. This is important, otherwise ADF Binding will fail to retrieve attributes from Page Definition on runtime - DepartmensView (Id attribute maps with Id, Name attribute maps with Name):
Same for JobsView, only difference - Name attribute maps with Title:
AM Data Model includes only DataView, only proxy dummy VO is accessible from ADF UI in our case:
There is custom method exposed in AM, this method will be invoked as Task Flow initializer and prepare real VO to be used through proxy DataView VO:
Here is main logic for sample application - changeVOInstance(voName) method:
This method logic:
1. Get proxy DataView VO
2. Remove proxy DataView VO definition
3. Find real VO definition by path name using ViewDefImpl.findDefObject(voName) function
4. Substitute proxy DataView VO with real VO definition, by creating real VO instance under proxy VO name using createViewObject("DataView", voDefImpl)
Method is invoked as initializer method from Task Flow:
Task Flow accepts VO path name parameter, it is passed as parameter to custom method:
Task Flow parameter is passed through PageFlow scope and Task Flow is set to be refreshed when parameter value changes (ifNeeded):
ADF UI is based on typical ADF UI component, no dynamic components - means highly customizable:
ADF UI bindings are pointing to Page Definition, where mapping with proxy DataView is defined:
On runtime, by default we render editable JobsView (Id and Title labels) - Jobs data:
Click Open Departments button - proxy DataView will be switched and DepartmentsView will be rendered instead (Id and Name labels) - Departments data:
This approach allows to edit and update rendered data:
As you can see, just with one ADF screen we have implemented two use cases - Jobs and Departments editable UI's.
Good post, Andrejus!
ReplyDeleteYou said dynamically iterator binding in Page Definition works only for Master-Detail VO instances. What did you mean?
I believe in this example you could use the following technique:
For sure, in such case, you have to have “real” VO instances in the AM.
No, dynamic binding works only when switching between same VO different instances.
ReplyDeleteAndrejus
???
ReplyDeleteIn your PageDef set
iterator Binds="#{pageFlowScope.voName}" RangeSize="25"
and it works.
Moreover, in such case your VO instances work in normal way (they save current row, uncommitted data, ...)
Exactly this doesnt work, when you have different VOs :) i was testing this.
ReplyDeleteAndrejus
If for you prefer another approach and it works in your case, i have blogged it as well. There is pointer in the post.
ReplyDeleteAndrejus
You can run into some problem with dynamic iterator binding when you show data in af:table. Because DefNode attribute in the PageDef's tree.nodeDefinition points to the particular VO's definition. And you really get empty table (in case of using another VO's instance). So, just leave the DefNode attribute empty or remove it at all.
ReplyDeleteYes, but leaving node binding empty, may cause proper key selection problem, this is more like workaround to me. While with proxy VO, you can change node definition programmatically always to be the same - my next blog post. I recommend to control data structure from ADF BC itself, is easier to maintain in centralized way, instead of using EL.
ReplyDeleteAdditionally, table dynamic node binding is not working, not just because it points to specific vo definition, but because it doesnt accept expression language. It would work otherwise, simply it cant read page flow scope expression. Same is valid for other cases, for example if we have custom method pointing to the VO, etc.
ReplyDeleteFor use case with tables and proxy VO, in order to keep generic binding in Page Def - we can set it while switching from one VO to another inside AM method. Call setFullName(name) function for retrieved ViewDefImpl and set it to point to DataView:
ReplyDeleteViewDefImpl proxyVoDef = ViewDefImpl.findDefObject(voName);
proxyVoDef.setFullName("com.redsamurai.model.views.DataView");
vo = this.createViewObject("DataView", proxyVoDef);
Node Binding points to proxy VO:
nodeDefinition DefName="com.redsamurai.model.views.DataView" Name="DataView0"
I also like dynamic iterator approach, but I think proxy VO gives more centralized control over what is rendered in UI.
Andrejus
Hi Andrejus,
ReplyDeleteis proxyVoDef.setFullName("com.redsamurai.model.views.DataView");
sets the value of
DefName
is page def to
"com.redsamurai.model.views.DataView"
Hi Andrejus!
ReplyDeleteCan we do something similar for master-detail tables.
I mean can we create a proxy for master and detail table and dynamically associate the VO instances.
Its much simpler for Master-Detail, check here - http://andrejusb.blogspot.com/2010/04/dynamic-iterator-binding-to-reuse-view.html
ReplyDeleteAndrejus
Hi Andrejus,
ReplyDeleteGreat article. I was looking for a proxy pattern for VO's and I came across your excellent article. I'm trying this technique with slightly more complexity but I am getting an error. In my case, the 2 VO's are also linked other view objects (the same for both VO's) through view links. When I try to remove one of the VO, I'm getting a java exception saying that it cannot be removed, because it's still being referenced. Do you know how to resolve this? I'm thinking of removing the view links, but then I need to remove the other view objects first. But then I have to re-create them also. Not sure if this is possible.
Hi,
ReplyDeleteYou may apply complete ADF dynamic generation, it might work for you: http://andrejusb.blogspot.com/2012/11/adf-generator-for-dynamic-adf-bc-and.html
Andrejus
Hi Andrejus,
ReplyDeleteThanks for your suggestion. BTW your example didn't work for JDeveloper 11.1.1.6.0. I redeveloped the example from scratch for this version of Jdeveloper. If you're interested I can send it to you.
I didn't have to use complete ADF dynamic generation, because your example works fine even with vo's that have view links, but for updating only. If I try to insert or delete through an AM Exposed client method, I get JBO-27023: Failed to validate all rows in a transaction whenever I get to the commit() statement. Would you know how to solve this? Thanks
Hi,
ReplyDeletePlease send sample app you are trying to test - I will take a look.
Yes - this sample is implemented with ADF 11g R2, it can be downgraded to previous release.
Andrejus
Hi Eugene,
ReplyDeleteI was testing recently your proposed way of removing nodeDefinition for the table - it doesnt work properly. Well, it works for initial table rendering, but if you decide to switch to another VO, ADF 11g R2 complains about invalid node definitions after refresh.
Andrejus
Thanks for the pointers here Andrejus. The table binding was a new learning experience for me. I'm glad you took the time to describe this in this posting.
ReplyDeleteThanks again,
BradW
Thanks Andrejus.. nice topic..
ReplyDeletei tried to implement such nice idea but am experiencing a new problem has to do with passivation/activation of application module. this approach initialized VO dynamically remove dummy VO with actual VO. what kind of data that should be passivated/activated for such dynamic VOs.
Hi Andrejus, nice post, thank you.
ReplyDeleteI have done an example with dynamic VO dummy and Dynamic Components, but when I execute the application with the AM pool disabled I have errors on Activation/Passivation.
Any recomendation? Thank you in advance