JDeveloper: Navigation after Return from Bounded Task Flow

A question on the OTN JDeveloper and ADF ‘Space’ asked for a sample and/or tutorial on how to navigate after a bounded task flow, based on pages, returns from its work.
I setup a sample application to show how this works. The application is built using JDeveloper 11.1.1.7.0 and uses the HR DB schema. The sample can be loaded using the link provided at the end of the post.
Before we start let’s take a look at the running application:


Now that we have seen how the application works let’s check out how it’s implemented.

We start with an unbounded task flow (adfc-config.xml) which look like

Unbounded Task Flow: adfc-config.xml

Unbounded Task Flow: adfc-config.xml


We see that the application is built from two pages, a ‘Start’ page and an ‘End’ page. The ‘Start’ page can call a bounded task flow employee-btf. The start page holds one button ‘Start’ which calls the bounded task flow which is shown below.

Bounded Task Flow: employee-btf.xml

Bounded Task Flow: employee-btf.xml

This task flow shows a table of employees and allows to navigate to a detail page where the employee data can be changed. Depending on the result of the change, we navigate back to the start page (when the rollback button is clicked) or we navigate to the ‘end’ page if the changes are committed (using the commit button). The buttons are calling the navigation case named ‘start’ or ‘end’ which are task flow return cases which are looking like

Task Flow Return: Start

Task Flow Return: Start

or

Task Flow Return: End

Task Flow Return: End

As you see the return for ‘start’ calls a parent navigation ‘start’ which is implemented using a ‘wild card’ navigation ‘*’. Same is true for ‘end’ task flow return call. Once the navigation is given back to the parent task flow, it looks for a navigation with the name of the outcome ‘start’ or ‘end’ and executes the navigation.

The sample can be loaded from the ADF EMG Samples workspace BlogUBTBTFNavigation.zip

JDEV 11.1.2.1.0: Using router to conditionally set navigation target

Interesting question came up on OTN ADF froum.
You have one page (page2) which is called from two other pages (page1 and page3). The question is how to set up the navigation in page2 so that you have only on button (back) which gets you back to the page the user navigated from to page2?

There is more then one solution to this problem. In this blog entry I show the declarative solution, so no java code is used. Here is adfc-config.xml which shows the navigation:

Router Back Navigation

Router Back Navigation

This is a simple navigation where the user can navigate from Page1 or Page3 to Page2. Page2 uses only one navigation case ‘back’ and let the router decide where to go. To make this happen, the button which navigates from Page1 to Page2 needs to store a hint ‘1’ in pageFlowScope which the router can check to decide where to go to. For this I add a af:setPropertyListener to store the hint in pageFlowScope. You don’t need a bean to store the value as the storage for the value is set up automatically.

                        <af:commandButton text="Page 2" id="cb1" action="page12">
                            <af:setPropertyListener from="#{'1'}" to="#{pageFlowScope.backTarget}"
                                                    type="action"/>
                        </af:commandButton>

The same technique is used in Page3 to store the hint ‘3’ in pageFlowScope

                        <af:commandButton text="Page 2" id="cb1" action="page32">
                            <af:setPropertyListener to="#{pageFlowScope.backTarget}" type="action"
                                                    from="#{'3'}"/>
                        </af:commandButton>

Finally the magic is done in the router. Here the value which is stored in pageFlowScope variable ‘backTarget’ is checked and hte correct navigation target is used for the back navigation. Below is the source of the router, the design view is shown in the first picture.

  <router id="backRouter">
    <case id="__12">
      <expression>#{pageFlowScope.backTarget eq '1'}</expression>
      <outcome>page21</outcome>
    </case>
    <case id="__13">
      <expression>#{pageFlowScope.backTarget eq '3'}</expression>
      <outcome>page23</outcome>
    </case>
    <default-outcome>page21</default-outcome>
  </router> 

When you run the sample you start from Page1 and navigate to Page2 you see the output text which shows the content of the pageFlowScope variable, ‘1’ in this case.

Navigation from Page1 to Page2

Navigation from Page1 to Page2

If the user navigates from Page3 to Page2 the output looks like

Navigation Page3 to Page2

Navigation Page3 to Page2


the output text shows ’3′ in this case.

You can download the sample application which is build using JDeveloper 11.1.2.1.0 from here BlogRouterBackNavigation.zip.
Please rename the file to ‘.zip’ after downloading it!
The sample don’t use any db connection and should be runnable on older JDeveloper versions too.

JDeveloper: Don’t overlook warning signs in task flows or check your ‘Show Warning’ settings

This week I came across a hard to find error using task flows, which was caused by a simple reason.

I tired out Frank Nimphius samples #52 about using task flows which are not part of the application itself, but which are added from adf libraries to the application. First I tried the sample Frank provided in the Code Corner article, which worked as expected after changing the db connections to my environment.

Running Sample #52

Running Sample #52

Next I’d build my own task flow in an adf library and tired to use it in the parent application as reagion. All went OK till I run the application. Here is the picture

My application with Task Flow in Library

My application with Task Flow in Library

As you can see the region representing the task flow from the library is not shown or empty. The first step to analyze the error was to inspect the task flow in the library which looked like:

Rebuilded task flow in the adf library

Rebuilded task flow in the adf library

This looked OK for me and a further visual inspection of each of the other elements did not show any difference compared to Franks sample. Here are the images of the method call activity

Method call setdeptId

Method call setdeptId

and the navigation together with the navigation target

Navigation and target page

Navigation and target page

After some more checking I saw the hint which finally lead me to finding the error. In the image above you see a yellow or brownish frame around the ‘From Outcome’ field. I didn’t pay much attention to this at first, but after checking the ‘setdeptId’ method call outcome and the navigation targets ‘From Outcome’ I saw the difference.
The ‘setdeptId’ method call had ‘setdeptId’ set as ‘Fixed Outcome’ whereas the navigation was set to ‘goEmployees’. Once these two attributes are set to the same value the the color frame is gone.

Navigation target after correction

Navigation target after correction

Some questions still to answer:
(q1) Why don’t you get any error output in the log when running the application?
(q2) Why do you see no other error or warning in the design view in JDeveloper?

The answer to (q1) is easy once you found the reason for the error. As the method call activity (setting the department id and executing the query) did not throw an error, the navigation took place. As the outcome was set to not existing target, the region remains empty. A this could be desired behavior and no exception occurred no log output is generated.

Question (q2) took some more research. Lately I wrote a couple of blog entries with many pictures from the samples I used to show something. Some of of the images I used showed a small yellow attention sign when e.g. a page element on a task flow when the page is not created jet. As I took some of the images before the pages where ready and I didn’t want to show the warnings in my pictures, I turn the warnings off. This is not a problem as I later created the missing pages. But I forgot about this change with the result that the visual warning for the error I described here was hidden.
With the warning turned on again the error is easy to detect:

Task Flow in the library with 'Show Warnin' on

Task Flow in the library with 'Show Warnin' on

To turn the ‘Show Warning’ on or off you drop down the ‘Show’ list box in the diagram view of a task flow or page:

'Show Warnig'

'Show Warnig'

So, always remember to reset settings you only change for your convenience or make sure you never forget their impact.

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!

Globale Navigation mit Menus in TemplatePages

In meinem aktuellen Projekt trat das Problem auf, dass eine Navigation über eine Menustruktur ermöglicht werden soll, die in allen Seiten der Anwendung zugänglich sein soll. Leider ist die Anwendung so groß, dass die UseCases gruppiert wurden und in eigene TaskFlows ausgelagert werden müssen (das entspricht wohl auch der ‘Best Practice’).
Leider stellen wir fest, dass innerhalb eines Taskflow (bounded) kein Zugriff auf Regeln des aufrufenden Taskflow (i.a. adfc-config.xml) möglich sind, da der Taskflow in sich abgeschlossen ist. Alle Seiten werden von einer TemplatePage abgeleitet, die das Menu für die globale Navigation als Facette in jede Seite einbaut. Damit braucht die Navigation nur einmal in dem Menufragment verdrahtet zu werden.
Nach der Suche nach Lösungen gab es aus meiner Sicht nur zwei Möglichkeiten:

  1. Eine Seite in der adfc-config.xml die das Menu für die globale Navigation enthält, die einzelnen Taskflows, die die Usecases darstellen werden immer in dieser Seite als Region eingebunden
  2. Die Navigation über das Menu muss in allen Seiten zugänglich gemacht werden, auch wenn eine Seite in einem Taskflow (gegebenenfalls geschachtelt) eingebaut ist.

Lösung 1 habe ich nicht weiter untersucht, da mir die Erfahrung im Umgang mit Regions fehlt. Lösung 2 sollte eigentlich die einfachere sein. Wir sind doch wohl nicht die einzigen Anwender mit der Anforderung einer global zugreifbarer Navigation über eine Menuleiste, die in einem Fragment eingebaut ist.

Leider hat die Suche nach einer Lösung wie in 2. beschrieben kein Resultat hervorgebracht. Nach einigem Probieren bin ich auf die im Folgendem beschriebene Lösung gekommen:

Jeder Menueintrag wird an eine Bean im Session Scope gebunden. Die Bean merkt sich nur welcher Menueintrag gewählt wurde (über einen an die Bean gebundenen ActionLisener pro Menueintrag). Weiterhin enthält die Bean eine Methode, die die Kennung des Menueintrag der als letzter gewählt wurde zurückliefert. Die Kennung dient dann später als Sprungziel.

Jeder Taskflow enthält eine Wildcard-Regel, die immer einen festen Outcome (z.B. fertigMenuauswahl) liefert, der auch genau über diesen Namen angesprungen werden kann.

Wildcrd Regel zur Menunavigation

Wildcard Regel zur Menunavigation

Bei der Auswahl eines Menueintrag muss jetzt nur nuch als Action der oben definierte Outcome geliefert werden (hier fertigMenuauswahl). Als XML sieht dies dann so aus:

<control-flow-rule>
<from-activity-id>*</from-activity-id>
<control-flow-case>
<from-outcome>fertigMenuauswahl</from-outcome>
<to-activity-id>menuauswahl</to-activity-id>
</control-flow-case>
</control-flow-rule>

Ein Menueintrag stellt sich im Propertyinspector so dar:

Propertyinspector eines Meuneintrag

Propertyinspector eines Meuneintrag

oder im Code:

<af:menuBar>
<af:menu text=”Stammdaten”>
<af:commandMenuItem text=”Standardsätze” actionListener=”#{MenuBean.menuauswahlStandardsaetze}”
action=”fertigMenuauswahl”/>
<af:commandMenuItem text=”Quellen”
actionListener=”#{MenuBean.menuauswahlQuellen}”
action=”fertigMenuauswahl”/> …
Der Code in der Bean ist:

public class MenuBean
{
String mMenuauswahl = null;

public void menuauswahlStandardsaetze(ActionEvent actionEvent)
{
    mMenuauswahl = "menustandardsaetze";
}
public String handleMenuAuswahl()
{
    return mMenuauswahl;
}
}

Als letztes Glied in der Kette fehl nur noch die eigentliche Durchführung der Navigation im unbounded Taskflow (adfc-config.xml). Dies geschieht wieder durch eine Wildcard-Regel, die mit der ID, die der Menuauswahl geliefert wird (fertigMenuauswahl) eine Methode in der Bean anspringt, die als Ergebnis das zuletzt ausgewählte Menu zurückliefert.

Ausschnitt der adfc-config.xml

Ausschnitt der adfc-config.xml

Der Methodencall wird so verdrahtet:

Propertyinspector handleMenuauswahl

Propertyinspector handleMenuauswahl

Damit können globale Navigationen über das Menu in allen Seiten, auch solchen in Taskflows, die auch noch geschachtelt sein können, durchgeführt werden.

Es ist allerdings noch zu berücksichtigen, dass in der Bean gegebenenfalls noch ein Rollback durchzuführen ist, damit in einem Taskflow gemachte Änderungen zurückgenommen werden, falls sie nicht expliziert gespeichert wurden.