Just to remember: Bean Data Controls allow to create data bound pages based on data provided by plain java objects. ADF Data Controls provide an access to data through binding layer a unified declarative way regardless of implementation details.
One of built-in aspects of binding layer is caching: you might already noticed one property of Iterator (visible in Property panel when Iterator is selected in a page definition): Cache results: <default> true.
It is an expected behavior in most of the cases which results in performance gains and is provided by ADF framework by default without an additional development efforts.
In case the data behind a Bean Data Control is modified in java directly an additional development step is needed to refresh an appropriate iterator and get the result of modifications visualized in UI component based on the iterator.
The sample application, available to download and run in it JDeveloper R2, is simple in memory implementation of CRUD+ use case for ADF table based on Bean Data Control and java collection of domain objects (in our case collection of Persons).
The symbol + stands for extension of CRUD with "copy Person" functionality in order a) to create new Person based on selected one in a table and b) to show how to refresh an iterator (personListIterator) after a new Object (Person) is added to a collection in java.
Lets take a look at the sample application in order to better understand the issue with refresh first.
Short description of CRUD+ sample - using standard functionality of ADF Bean Data Control
There is one Bounded Task Flow, named persons-task-flow-definition, containing one page fragment with one ADF table in it:
The table is based on iterator personList from flowController and CRUDpersonsTaskFlowDataControl (see previous blog post with a description of Flow Scoped Data Control for more details regarding this technique):
The buttons Create and Delete are based on standard ADF Operations (created by Drag&Drop) - nothing exciting, it just works. The button Copy selected person was created by D&D of copyPerson method from data control into a page and providing a currentRow.dataProvider of personList iterator.
An appropriate java class behind a data control , named CRUDPersonsFlowController , contains a collection personList and implementation of method copyPerson:
This blog post provides an example of this issue and shows one simple technique of "how to refresh without side effects" - not loosing some other functionality of the framework, like current row - for Bean Data Control.
The symbol + stands for extension of CRUD with "copy Person" functionality in order a) to create new Person based on selected one in a table and b) to show how to refresh an iterator (personListIterator) after a new Object (Person) is added to a collection in java.
Lets take a look at the sample application in order to better understand the issue with refresh first.
Short description of CRUD+ sample - using standard functionality of ADF Bean Data Control
There is one Bounded Task Flow, named persons-task-flow-definition, containing one page fragment with one ADF table in it:
The table is based on iterator personList from flowController and CRUDpersonsTaskFlowDataControl (see previous blog post with a description of Flow Scoped Data Control for more details regarding this technique):
An appropriate java class behind a data control , named CRUDPersonsFlowController , contains a collection personList and implementation of method copyPerson:
To complete a brief description of the sample, a snippet of domain object used , Person.java, looks like this:
And the page definition at this stage contains bindings created implicitly as a result of our Drag&Drop declarative efforts:
Note: recent blog post of Depac C S shows an alternative ADF java API based way of how to create table rows based on existing row content.
The issue with "Refresh" related to personListIterator
Our sample behaves almost like expected at this time: one click on create button and new empty row is created, by issuing a click on delete we delete a selected person from a list.
The refresh issue shows up on using a functionality of copyPerson: we click on a button Copy Selected Person, method copyPerson in flow controller java class gets executed, we get the person selected as input in a java method, create new instance of domain object Person using copy constructor add the instance created into personList.
Expected result: the table contains one person more - a clone of a selected person.
Actual result: nothing. Everything looks the same like before issuing a copyPerson action.
The table on a page is not refreshed automatically after doing a modification of data behind it in java. It shows old (cached) data of personList.
We need to take care about "refresh" by issuing an execute on personList iterator to align the contents of collection represented in java and cached also as peronListIterator previous execution result.
Using contextual events to refresh personList iterator
In order to fix the issue, we go to the page definition of editPersonList.jsff and create a new Execute binding for action on personListIterator first: open editPersonList.jsff, click on a + in a panel bindings , select the item to be created: action, ok and set the properties Select an iterator and Operation like this:
We get an Execute action binding as a result of this effort:
Now we just only have to issue this Execute action after the execution of copyPerson in order to "renew" the content of personListIterator.
We can achieve that by using build-in contextual events of ADF in two simple steps:
Creating copyPersonEvent contextual event on copyPerson action binding
Select copyPerson in a Panel Structure of editPersonListPageDef.xml , Right-click - Insert Inside copyPerson -> events:
An element Events is created. Select it and create a new Event by issuing a right-click -> Insert inside events - events:
Provide a name for the event in a panel:
The Events section in a Structure Panel looks after that like this:
The event copyPersonEvent gets fired once copyPerson action is executed. There are no subscribers for the event at this stage.
Lets subscribe an action Execute of personListIterator to listen to the event to renew the iterator content.
Subscribing action binding Execute of personListIterator to contextual event
We go back to Page Data Binding Definition, select a tab Contextual events, then select copyPersonEvent (the event was created in a previous step), and select subscribers tab on a right side:
Click on a + symbol to create a new subscription. In a panel subscribe to Contextual Event pick or enter the properties Event, Publisher and Handler like this:
And the page definition at this stage contains bindings created implicitly as a result of our Drag&Drop declarative efforts:
Note: recent blog post of Depac C S shows an alternative ADF java API based way of how to create table rows based on existing row content.
The issue with "Refresh" related to personListIterator
Our sample behaves almost like expected at this time: one click on create button and new empty row is created, by issuing a click on delete we delete a selected person from a list.
The refresh issue shows up on using a functionality of copyPerson: we click on a button Copy Selected Person, method copyPerson in flow controller java class gets executed, we get the person selected as input in a java method, create new instance of domain object Person using copy constructor add the instance created into personList.
Expected result: the table contains one person more - a clone of a selected person.
Actual result: nothing. Everything looks the same like before issuing a copyPerson action.
The table on a page is not refreshed automatically after doing a modification of data behind it in java. It shows old (cached) data of personList.
We need to take care about "refresh" by issuing an execute on personList iterator to align the contents of collection represented in java and cached also as peronListIterator previous execution result.
Using contextual events to refresh personList iterator
In order to fix the issue, we go to the page definition of editPersonList.jsff and create a new Execute binding for action on personListIterator first: open editPersonList.jsff, click on a + in a panel bindings , select the item to be created: action, ok and set the properties Select an iterator and Operation like this:
We get an Execute action binding as a result of this effort:
Now we just only have to issue this Execute action after the execution of copyPerson in order to "renew" the content of personListIterator.
We can achieve that by using build-in contextual events of ADF in two simple steps:
- Issuing a contextual event copyPersonEvent upon execution of copyPerson action and
- Subscribing an Execute action of personListItertor as listener of it.
Creating copyPersonEvent contextual event on copyPerson action binding
Select copyPerson in a Panel Structure of editPersonListPageDef.xml , Right-click - Insert Inside copyPerson -> events:
An element Events is created. Select it and create a new Event by issuing a right-click -> Insert inside events - events:
Provide a name for the event in a panel:
The Events section in a Structure Panel looks after that like this:
The event copyPersonEvent gets fired once copyPerson action is executed. There are no subscribers for the event at this stage.
Lets subscribe an action Execute of personListIterator to listen to the event to renew the iterator content.
Subscribing action binding Execute of personListIterator to contextual event
We go back to Page Data Binding Definition, select a tab Contextual events, then select copyPersonEvent (the event was created in a previous step), and select subscribers tab on a right side:
Click on a + symbol to create a new subscription. In a panel subscribe to Contextual Event pick or enter the properties Event, Publisher and Handler like this:
A new Element Event Map gets created as a result , containing a list of subscribers to contextual events.
We can see Event Map in a Structure panel now:
Bonus - using copyPersonEvent to select a new row containing the person created by copyPerson action
As a bonus for this blog post - additional action binding for the built-in operation Last of personListIterator was created and subscribed to the same event.
It shows that more than one subscriber to the event can be defined and provides one use case of this feature: to position a current row on a person copied in table on a page. The steps already described in previous chapters were used.
Behavior of our sample application
We start the sample application and create a new row first by a click on Create button. Lets name the person created Mister Smith:
Click on Submit. New instance of Person object gets added to the collection of persons.
Now we clone Mr. Smith by a click on Copy selected person
(Remember The Matrix? There were a lot of them at the end :)
Our Mister Smith is cloned and displayed at the end of the table (execution result of copyPerson, fire
copyPersonEvent , Execute on personListIterator as subscriber for copyPersonEvent).
And the bonus -cloned Mister Smith is selected in a table as currentRow ( execution result of Last action on personListIterator as subscriber to copyPersonEvent):
Conclusion
- The sample application shows simple in memory implementation of CRUD+ functionality by using a built-in standard functionality of ADF Bean Data Control declarative way
- The issue with "refresh" was compensated by using built-in ADF framework functionality of contextual events declarative way
- The plain java code of flow controller is supposed to facilitate an easy reading , testing out of context (no dependencies to additional APIs) and understanding of it - it is an important aspect of every enterprise system regarding (usually) a long lifecycle of them.
The sources of the sample application are provided at github.
Check also ADF EMG Samples page for additional sample applications covering broad range of ADF.
Perfect tutorial. Thank you!!
ReplyDeleteBest on the web!!!
ReplyDelete