Lately a couple of questions on the JDeveloper & ADF space regarding using task flow parameters came up.
Use Case
One specific use case was how to show different UI in the same region if a row is just created or if the user wants to edit an already existing row.
Full description is that the user sees a table with e.g. regions of the HR DB schema. Now there are two buttons, one ‘Create new…’ and one ‘Edit current…’. When clicking the ‘Edit current…’ button the currently selected row of the table should be loaded into a form. There the user can edit everything but the primary key (PK). If the user click the ‘Create new…’ button the same form should be visible, but the PK should be editable too.
Running Application
To make it more visible let’s start with the finished application:
The final UI looks like in the image above. The UI is composed of four areas as in the image below:
The ‘Header’ and ‘Search Panel’ area are only for convenience. In the ‘Panel Collection Bar’ holds a toolbar with two buttons ‘Create new…’ and ‘Edit current…’. The table below shows the result of a search of the Region table from the HR DB.
Selecting a row in the table we can edit the selected record by clicking on the ‘Edit current…’ button
This will open a new screen showing the selected row. Above the ‘RegionId’ we see a text indicating that we are in ‘edit’ mode and we can’t edit the ‘RegionId’ attribute as it’s the PK of the row and should not be editable.
Here we can edit the RegionName attribute and store the change by clicking the ‘Commit’ button:
Likewise, if we click the ‘Create new…’ button we go to the same form, but this time the text above the ‘RegionId’ attribute tells us that we are in ‘create’ mode and we can edit the RegionId.
Committing the changes we get a new row in the Regions HR DB table.
Implementation
Ok, let’s talk about how to implement this. For the model layer we run the ‘Business Components from Table…’ wizard on the model project and select the regions table from the HR DB. For this demo this is all we need to do.
The UI consist of two pages, index.jsf and Region.jsf. The index.jsf page is the start page and shows the UI as in the first image. Everything is easily done by drag and drop the right components in the right order onto the page. I spare the details for this as you can look at the sample which you can download using the link at the end of the post.
The only thing I like to go into detail is the toolbar with the two buttons ‘Create new…’ and ‘Edit current…’. These buttons do two things:
- Set a mode property to pageFlowScope
- Navigate to the second page Region.jsf
The Toolbar definition looks like
<af:toolbar id="t1"> <af:button text="Create new..." id="b1" action="show"> <af:setPropertyListener from="#{'create'}" to="#{pageFlowScope.mode}" type="action"/> </af:button> <af:button text="Edit current..." id="b2" action="show"> <af:setPropertyListener from="#{'edit'}" to="#{pageFlowScope.mode}" type="action"/> </af:button> </af:toolbar>
The create button has a af:setPropertyListener added which sets a pageFlowScope attribute ‘mode’ to ‘create’ and navigates to the Region.jsf page by executing the ‘show’ navigation from the unbounded task flow adfc-config.xml
The edit button uses an af:setPropertyListener which sets a pageFlowScope attribute ‘mode’ to ‘edit’ and then executes the navigation ‘show’ to go to the Region.jsf page. The logic to insert a new row or to edit an existing row is done in the bounded task flow ‘region-edit-create-btf.xml’ which we talk about later.
The Region.jsf page consists of a Header and a Region holding an af:form of the selected row of the Region:
The region itself is a bounded task flow with the following properties
Here we see one parameter with the name ‘mode’ which stores its value in a pageFlowScope attribute named ‘mode’. One other thing we need to make sure of is that the region shares the data control with its parent (in this case the adfc-config unbounded task flow) and always begins a new transaction. This make the bounded task flow a unit of work, it encapsulates the work in the task flow. The interface of the bounded task flow describes what the unit of work does:
Interface of ‘region-edit-create-btf.xml’ task flow:
If mode is set to ‘edit’, the current selected row of the Region table is shown in a form and can be edited.
If the mode is set to ‘create’, a new row is created and inserted into the Region table and can then be edited.
The user can commit or cancel the operation. After each of this operations the task flow executes a parent action ‘back’.
We see that the default action of the task flow is a router which uses the parameter set to the task flow to execute the create of the edit navigation:
after that the now current record is shown on the fragment (see the area marked ‘Region’ in image Region.jsf). Below we see the panelFormLayout used for the region:
<af:panelFormLayout id="pfl1"> <af:outputText value="we are in #{pageFlowScope.mode eq 'create'? 'create' : 'edit'} mode" id="ot1"/> <af:inputText value="#{bindings.RegionId.inputValue}" label="#{bindings.RegionId.hints.label}" required="#{bindings.RegionId.hints.mandatory}" columns="#{bindings.RegionId.hints.displayWidth}" maximumLength="#{bindings.RegionId.hints.precision}" shortDesc="#{bindings.RegionId.hints.tooltip}" id="it1" disabled="#{pageFlowScope.mode ne 'create'}"> <f:validator binding="#{bindings.RegionId.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.RegionId.format}"/> </af:inputText> <af:inputText value="#{bindings.RegionName.inputValue}" label="#{bindings.RegionName.hints.label}" required="#{bindings.RegionName.hints.mandatory}" columns="#{bindings.RegionName.hints.displayWidth}" maximumLength="#{bindings.RegionName.hints.precision}" shortDesc="#{bindings.RegionName.hints.tooltip}" id="it2"> <f:validator binding="#{bindings.RegionName.validator}"/> </af:inputText> <f:facet name="footer"> <af:panelGroupLayout id="pgl2"> <af:button text="Commit" id="b2" action="commit"/> <af:button text="Rollback" id="b1" immediate="true" action="rollback"> <af:resetActionListener/> </af:button> </af:panelGroupLayout> </f:facet> </af:panelFormLayout>
Let’s look at the actions which are done in the region. If the user commits the changes the commit action from the data control is called which saves the changes to the db. If the ‘cancel’ button is clicked, the rollback method from the data control is called which reverts any changes done in the task flow. After the commit or rollback a parentAction (paraneAction1) is called which executes the ‘back’ navigation in the adfc-config.xml which navigates back to the index.jsf page.
Please note that we could have added the calls to commit and rollback to the buttons in the region.jsff. I decided to put them into the task flow instead to show the whole task flow and how it works in one place.
Implement different UI according to the task flow parameter
So, how do we use the parameter passed to the bounded task flow to switch the UI?
This is done by using an expression language (EL) which points to the ‘mode’ attribute stored in the pageFlowScope. Sample: the text above the RegionId is created with an af:outputText like
<af:outputText value="we are in #{pageFlowScope.mode eq 'create'? 'create' : 'edit'} mode" id="ot1"/>
The EL ‘#{pageFlowScope.mode eq ‘create’? ‘create’ : ‘edit’} ‘ is used to differentiate between the modes. Likewise the disable property of the RegionId attribute uses the EL
...disabled="#{pageFlowScope.mode ne 'create'}"...
which is true when the passed parameter is not ‘create’. In this case the disabled property is set to false, meaning that the field can’t be edited.
That’s it. There is no line of java code necessary to implement this use case.
Download
You can download the sample which was build using JDeveloper 12.2.1.2 and uses the HR DB schema from GitHub BlogTaskFlowParameter.