JDeveloper: Using Task Flow Parameters to Show Different UI in a Region

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:

selection_955

Running Application

The final UI looks like in the image above. The UI is composed of four areas as in the image below:

selection_955_comment

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

selection_956

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:

  1. Set a mode property to pageFlowScope
  2. 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

adfc-config.xml

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:

selection_957_comment

Region.jsf

The region itself is a bounded task flow with the following properties

selection_964

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’.

selection_965

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:

selection_966

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.

Advertisements

JDeveloper 11.1.1.6.0 Use Selection in LOV to Navigate to Detail

This post shows how a selection in a list of value (LOV) can be used to navigate to another page to show detailed information about the selected item in the LOV. The sample uses the HR db schema, the work space can be loaded using the link provided at the end of the post.

Use Case
Using a af:selectOneChoice showing the department names of the departments table we want to select on department. Then by clicking a button we want to navigate to a different page which shows the details of the selected department.

Let’s start with a look at the finished application:

Running Application

Running Application

After selecting a department from the af:selectOneChoice we see the index of the selected department.

Select Department

Select Department

This is shown in the outputText below the LOV. Selection ‘Human Resources’ selects the index 3 in the LOV.

Navigate to Detail Page

Navigate to Detail Page

And finally the detail Page where we see the correct department id for ‘Human Resources’ of 40.

Detail Page

Detail Page

Implementation
To implement this use case we define two view objects (VO). One which we use for the LOV of department names (DepartmentLOVView) and one which we use to show the detail on a form for the detail page (DepartmentView1).

Data Model

Data Model

In the UI we define a bounded task flow (lov-select-detail-btf.xml) which is build using fragments. This task flow is put on a page (Start.jspx) as region.

lov-select-detail-btf

lov-select-detail-btf

Before we begin to setup the LOV we need a place to store the selected value from the LOV. For this we create a pageDef file for the DepSelect.jsff by right clicking on the page and selecting ‘Go to Page Definition’ from the menu. As there is no pageDef file one is created for us. We define a variable DepId inside the variable section of the ‘Exceutables’ section. Then we add an attributeValue ‘DepId’ in the bindings section.

Define Variable DepId inside the Executables Section

Define Variable DepId inside the Executables Section

Variable DepId

Variable DepId

Add attributeValue Binding

Add attributeValue Binding

Select DepId from variabels

Select DepId from variabels

To setup the LOV drag the DepartmentId from the DepaermentLOVView from the Data Controls section onto the DepSelecte.jsff fragment. Change the values in the dialog to match the image below:

Edit List Binding for DepartmentId

Edit List Binding for DepartmentId

In the property editor for the selectOneChoice set the label property to ‘Depaertment’ and the value property to ‘#{bindings.DepId1.inputValue}’ which is the attribute we defined in the variables section. Set the autoSubmit property to true so that selected values are posted into the variable once the value changes. The final selectOneChoise code is

        <af:selectOneChoice label="Department" id="soc1" required="#{bindings.DepartmentId.hints.mandatory}" value="#{bindings.DepId1.inputValue}"
                            autoSubmit="true" valuePassThru="true">
          <f:selectItems value="#{bindings.DepartmentId.items}" id="si1"/>
        </af:selectOneChoice>

To show the selected item we add an outputText which shows the “#{bindings.DepId1.inputValue}”, which is the place the value is stored after selecting a department in the LOV. Notice that we don’t see the DepartmentId (the PK of the VO), but the index of the selected department in the list binding. As we don’t use a value driven LOV we have to map the index back to the row key ourselves. One more reason to stick to model driven LOV whenever possible. In this use case we don’t use a model driven LOV by intent. This is to show how to map the index back to the row key of the list.

The missing element is a button we use to navigate to the next page, the detail page, showing the departments detail as a form (read only). To get this button, open the data controls section and then open the DepartmentsLOVView and open the ‘Operations’ node. Select the ‘setCurrentRowWithKeyValue’ operation and drag it onto the fragment. Drop it as operation->button. This will add the operation into the pageDef for the fragment. The image below shows the final pageDef:

Final pageDef

Final pageDef

The missing part is the mapping of the selected index to the needed key of the department. For this we select the button, go to the properties of the button. In the action property we select ‘Edit’ from the drop down list (small arrow) on the right side of the property. In the Dialog we create a new bean ‘DepSelectBean’ in the package ‘de.hahn.blog.lovselectdetail.view.beans’. In the method part we choose ‘new’ to create a new method ‘showSelectedDep’. Finally we change over to the source tab of the fragment and delete the ‘#{bindings.setCurrentRowWithKeyValue.execute}’ from the actionListener property. The reason for this is that we execute the method from the bean after we got the real DepartmentId from the LOV. The magic is done in the action method ‘showSelectedDep()’ in the bean.

    public String showSelectedDep() {
        BindingContext lBindingContext = BindingContext.getCurrent();
        BindingContainer bindings = lBindingContext.getCurrentBindingsEntry();
        // get the list binding for the department lov
        JUCtrlListBinding list = (JUCtrlListBinding)bindings.get("DepartmentId");

        // get the selected index from the list which is stored in the DepId1 attribute
        AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("DepId1");
        Integer selid = (Integer)attr.getInputValue();

        // load the listdata
        Object row = list.getDisplayData();
        // get the selected row from the list
        Row lFromList = (Row)list.getValueFromList(selid);
        // from the row we get the PK the DepartmentId
        Object lAttribute = lFromList.getAttribute("DepartmentId");
        Number newVal = (Number)lAttribute;
        _logger.info("Information: selected Department = " + newVal);
        // get the MethodAction for setCurrentRowWithKeyValue
        OperationBinding method = bindings.getOperationBinding("setCurrentRowWithKeyValue");
        // set hte needed parameter as the department id
        method.getParamsMap().put("rowKey", newVal);
        method.execute();
        // after execution check for errors
        List errors = method.getErrors();
        if (!errors.isEmpty()) {
            Exception ex = (Exception)errors.get(0);

            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, ex.getMessage(), "");
            FacesContext.getCurrentInstance().addMessage(null, msg);
            // keep on page in case of an error
            return null;
        }

        // navigate to the next page
        return "show";
    }

This concludes the implementation. The workspace can be loaded from ADF EMG Samples Project. The sample uses the HR db schema and was developed using JDeveloper 11.1.1.6.0

Using diferent VOs for Master Detail Navigation (the Declatative Way)

A user on the OTN forum asked a question how to do a master detail like navigation where the master VO is not equal to the detail VO and no accessors or link is available between the tow VOs.

A use case for this scenario is e.g. you have a read only table as master which holds an attribute which is the foreign key to an other table (the master table has a FK to the detail you like to change). In the sample I’m talking about in this blog I used the HR schema, the employees table as master and the the department as detail. I show how to use the employees as read only table, select an employee to edit the department the employee is assigned to.

Here is the data model of the sample:

Data Model

Data Model


As you can see there are no view links defined which could be used to navigate from the employee to the related department.

I’ll do all this the declarative way, so I don’t use a bean or other Java code. I use a bounded task flow and start with a query panel with the read only employees table. Each row shows the id of the employee, the name and the department id. I add a button to the department id of each row and use this to navigate to the departments edit page. Here you see the running app, the query panel which I used to select employees records and the button which I added to the department is column.

Start Screen

Start Screen

I used a button here because of an error in this version (11.1.2) of jdev which prevent the table from selection the current row when you just hit a link in a row. Frank Nimphius provided a workaround for this here:JDeveloper 11.1.2 : Command Link in Table Column Work Around. A click on the ‘Department’ button for ‘Jannette King’ will navigate to Department ’80’ which is editable

Select a Department from the Table and Edit Department

Select a Department from the Table and Edit Department

The work flow is implemented as shown below:

Work Flow

Work Flow

As you see the whole work is done in bounded task flow which first presents the query panel together with the resulting employees table (read only). The column ‘Department ID’ shows the button I use to navigate to the editable departments page. As there is no view link, it’s not enough to select the employees row to mark it as current row. I have to extract the department id from the selected row and use this to search for the department before showing the departments edit page.

I store the department id in a page flow scope variable named ‘#{pageFlowScope.depKey}’. If you like you can store the value of the department id elsewhere e.g. in the variables iterator of the page binding. To extract and store the value I use a af:setPropertyListener which allows to react on the action of the button and transfer the value to page flow scope variable. Here is the code of the department id column:

                            <af:column sortProperty="#{bindings.EmployeesView1.hints.DepartmentId.name}"
                                       sortable="true"
                                       headerText="#{bindings.EmployeesView1.hints.DepartmentId.label}"
                                       id="resId1c4" width="114">
                                <af:outputText value="#{row.DepartmentId}" id="ot5">
                                    <af:convertNumber groupingUsed="false"
                                                      pattern="#{bindings.EmployeesView1.hints.DepartmentId.format}"/>
                                </af:outputText>
                                <af:commandButton text="ShowDepartment" id="cb2" action="showDep">
                                    <af:setPropertyListener from="#{row.DepartmentId}"
                                                            to="#{pageFlowScope.depKey}"
                                                            type="action"/>
                                </af:commandButton>
                            </af:column>

The button action navigates to he method call ‘SetCurrentRowWithKeyValue’ in the bounded task flow. This method I dragged from the data control palette from the DepatermetnsView1 operation onto the bounded task flow definition page

SetCurrentRowWithKeyValue from DepartmentsView1

SetCurrentRowWithKeyValue from DepartmentsView1

The method searches the department using the the value stored in the page flow scope variable. The dialog below opens automatically when you drop the method on the task flow and lets me enter the key value to search for:

setCurrentRowWithKeyValue  Edit Action Binding

setCurrentRowWithKeyValue Edit Action Binding

Here is the pagedef file for the method call:

PageDef of setCurrentRowWithKeyValue Method

PageDef of setCurrentRowWithKeyValue Method


After the search the current row is set in the DepaertmensView1 and I can navigate to to the edit page. That’s about it.

You can download the sample work space from here Sample Workspace blogmasterdetaildeclarative_v2-zip. You have to rename the file to ‘.zip’ after download!