vrijdag 25 februari 2011

How to trigger an action on enter

Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES


Summary



In this blog a solution is provided how to trigger an action on enter. An action can be a command link or button. All defined actions (action, actionListener, showPopupBehaviour) will be executed when the user uses the enter.

To trigger an action set the defaultCommand property of the af:form tag. Set the value to the ID of the button / link that should be triggered on action.

We’ll use the following page in this blog:



This page is based on the EMPLOYEES table in the HR schema. The table is created by drag and drop from the Data control palette. An edit (commandLink) column is added to navigate to a form of the employee. The page contains 2 buttons, a Cancel which navigates back to the home page and create an insert which creates a now employee row and navigates to the form.

Trigger a button



In this example we’ll trigger the Cancel button, the cancel button asks for confirmation and if the user answers with OK it navigates back to the home page.

The button looks like this:

<af:commandButton text="Cancel"

id="cancel"

immediate="true">

<af:resetActionListener/>

<af:showPopupBehavior popupId="cancelPopup"

triggerType="action"/>

</af:commandButton>

Set the default command of the af:form tag to this ID. Change:

<af:form id="f1">

To:

<af:form id="f1" defaultCommand="pt1:cancel">

Where pt1 is the id of the page template that contains the cancel button.

When we run this and we press enter you get:



Trigger a link



In this example we’ll trigger the Edit command link, the link contains an actionListener which adds an information message to the stack and an action which navigates to the form.

First set the current row of the view object by selecting a row:



Then when enter is pressed:



The commandLink for this example looks like this:

<af:column headerText="Edit employee"

id="c12">

<af:commandLink text="Edit"

id="cl5"

actionListener="#{employeeBean.edit}"

action="edit"/>

</af:column>

The default command of the af:form tag is set to:

<af:form id="f1" defaultCommand="pt1:t1:cl5">

Where pt1 is the id of the page template and t1 is the id of the table that contains the command link column.

The employeeBean refers to a request scoped bean that contains an edit method:

public void edit(ActionEvent actionEvent) {

EmployeesViewImpl view = getService().getEmployeesView();

String name = view.getCurrentRow().getAttribute("FirstName") + " " + view.getCurrentRow().getAttribute("LastName");

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Edit", "Edit person: " + name.trim()));

}


private static HrAppModuleImpl getService() {

DCBindingContainer bc = (DCBindingContainer)FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{bindings}", BindingContainer.class);

DCDataControl dc = bc.findDataControl("HrAppModuleDataControl");

return (HrAppModuleImpl) dc.getDataProvider();

}

How to set the cursor on page opening

Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES


Summary



In this blog a solution is provided how to set the cursor in a specific field on page opening.

When a page with input fields is created by drag and drop from the data control palette it looks like this:



The user first has to click in a field before he can edit it.

But if we add into the af:document tag an initialFocusId clause referring to the component that should be active and set in that component the clientComponent property then the cursor is in that field on page opening.

In this example we changed:

<af:document id="d1"

title="emp">

To:

<af:document id="d1"

title="emp"

initialFocusId="pt1:it4">

With pt1:it4 is the id of the inputText EmployeeId (id it4) in the page template (id pt1).

We changed the input text employee ID from:

<af:inputText value="#{bindings.EmployeeId.inputValue}"

label="#{bindings.EmployeeId.hints.label}"

required="#{bindings.EmployeeId.hints.mandatory}"

columns="#{bindings.EmployeeId.hints.displayWidth}"

maximumLength="#{bindings.EmployeeId.hints.precision}"

shortDesc="#{bindings.EmployeeId.hints.tooltip}"

id="it4">

<f:validator binding="#{bindings.EmployeeId.validator}"/>

<af:convertNumber groupingUsed="false"

pattern="#{bindings.EmployeeId.format}"/>

</af:inputText>

To:

<af:inputText value="#{bindings.EmployeeId.inputValue}"

label="#{bindings.EmployeeId.hints.label}"

required="#{bindings.EmployeeId.hints.mandatory}"

columns="#{bindings.EmployeeId.hints.displayWidth}"

maximumLength="#{bindings.EmployeeId.hints.precision}"

shortDesc="#{bindings.EmployeeId.hints.tooltip}"

id="it4"

clientComponent="true">

<f:validator binding="#{bindings.EmployeeId.validator}"/>

<af:convertNumber groupingUsed="false"

pattern="#{bindings.EmployeeId.format}"/>

</af:inputText>

Now the page looks on opening like this:



The initialFocus clause can also be conditional.

For example, on creation of a new employee the cursor must be in employee ID, when an existing employee is edit the employee ID is not updatable and the cursor must be in firstname.

When the employee is created the employee ID is blank, when an employee is edited it’s filled. This fact is used to render the inialFocus. Change it to:

<af:document id="d1"

title="emp"

initialFocusId="#{bindings.EmployeeId.inputValue==null?'pt1:it4':'pt1:it2'}">

Add a readOnly clause to the employee ID input text:

<af:inputText value="#{bindings.EmployeeId.inputValue}"

label="#{bindings.EmployeeId.hints.label}"

required="#{bindings.EmployeeId.hints.mandatory}"

columns="#{bindings.EmployeeId.hints.displayWidth}"

maximumLength="#{bindings.EmployeeId.hints.precision}"

shortDesc="#{bindings.EmployeeId.hints.tooltip}"

id="it4"

readOnly="#{bindings.EmployeeId.inputValue!=null}"

clientComponent="true">

<f:validator binding="#{bindings.EmployeeId.validator}"/>

<af:convertNumber groupingUsed="false"

pattern="#{bindings.EmployeeId.format}"/>

</af:inputText>

And set the clientComponent property to true for the first name input text (id it2):

<af:inputText value="#{bindings.FirstName.inputValue}"

label="#{bindings.FirstName.hints.label}"

required="#{bindings.FirstName.hints.mandatory}"

columns="#{bindings.FirstName.hints.displayWidth}"

maximumLength="#{bindings.FirstName.hints.precision}"

shortDesc="#{bindings.FirstName.hints.tooltip}"

id="it2"

clientComponent="true">

<f:validator binding="#{bindings.FirstName.validator}"/>

</af:inputText>

Now the page looks for opening in insert or edit mode like this:

donderdag 24 februari 2011

How to never display a horizontal scrollbar in a table

Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES


Summary



Display an table layout in ADF11 is easy, but I’m annoyed by the horizontal scrollbar in the button of the table. A vertical scrollbar I can imagine, I believe it’s more user friendly then the page range that ADF10 had.

Let’s first see what ADF11 creates when dragging and dropping the Employees table from the data control palette. When the browser has a ‘reasonable’ size it looks like this:



Why is the table that small? The screen is so big I would prefer the table being wider so the vertical scrollbar can disappear. When we make the screen (very) small it’s even worse:




Now we have to scrollbars. The scrollbar underneath the Cancel button is just to make the vertical scrollbar of the table visible. But actually this scrollbar (the one underneath the Cancel button) is the one I would like to keep it’s the scrollbar of the complete page content (like word has a scrollbar if you make the screen smaller then a page).

This issues can easily be solved. Just put in the table:

styleClass="AFStretchWidth"

This is equal to width 100%. Then if we have a wide screen it looks like this:



But know I wonder, why such a use gap between the last column and the vertical scrollbar? If we make the screen small it looks like this:



Now we only have one scrollbar, but if we would add items underneath the table wider as the screen a second horizontal scrollbar will be displayed at the bottom of the page (content so above the Marianne Horsch © ADF footer).

So still I’m not really satisfied by how the table looks. Now look at this wide screen:



That looks fine! Scrollbar just after the last column just where I would expect it. But in a small screen??



Also just like I would like it, at most one horizontal scrollbar but not in the table but at the bottom of the page content.

But how did I achieve this? Well I set the width instead of the styleClass. To be precise, I set the width to 1276.

Calculate the width of the table



You may think 1276, how did she come up with that number? Trial and error? And does it really has to be exactly 1276?

First answer the second question:



This picture is width 1280, there is now a gap between the last column and the scrollbar. So yes it needs to be exactly 1276 (for this table).

Then the first question, was it trial and error? No it wasn’t, you can calculate the width of the table. And it is as follows:
  • Sum all the widths of the columns
  • Add to this the number of columns multiplied by 5
  • If the table has a vertical scrollbar then add 16
In this example this is:
  • Sum of all width of the columns is 1200 (12 columns each with width 100)
  • 12 columns multiplied by 5 is 60
  • We have a scrollbar so add 16
  • Makes total 1200 + 60 + 16 = 1276.

A generic solution



Of course, you don’t want to recalculate the width of the table every time you add or remove a column, or what if columns are rendered based on criteria such as user roles? And display a scrollbar or not is not fixed as well.

Before the solution is described let us first take a look at the example application:

Setup example application



For this blog an example application is created based on the HR schema. The example application contains an employees table with a filter. For this employees table a bounded task flow is created which is started from the menu.

Model layer



Create the following entities:

Entity name Based on table of HR schema Customizations made
Employee EMPLOYEES None


Create the following view objects:




View object name Based on entities Customizations made
EmployeesView Employee Added 2 bind variables:
  • b_min_salary (Number)
  • b_min_salary (Number)
Changed where clause:

(:b_min_salary IS NULL OR (:b_min_salary IS NOT NULL AND Employee.SALARY >= :b_min_salary)) AND (:b_max_salary IS NULL OR (:b_max_salary IS NOT NULL AND Employee.SALARY <= :b_max_salary))


Create an application module HrAppModule which exposes the EmployeesView.



Unbounded task flow



The unbounded task flow from where we start with the solution looks like this:



There are no customizations made, the task flow is created by drag and drop.

Bounded task flow



The employees task flow is a bounded task flows.



The next properties are set (for both bounded task flows):

Property Value
usePageFragments false

Share data controls with calling task flow true


Table page



The table pages are created by drag and drop from the Data Controls. The table is dropped as ADF Read-only table with Row Selection and Sorting checked, all columns are displayed.





Underneath the table a Cancel button (af:commandButton) is added which ends the task flow.

af:commandButton property Value
text Cancel
id cancel
action cancel
immediate true


Above the table a panelFormLayout is added that contains 2 input text items and a button.

The input text items have the following properties:

Property First input text Second input text
binding #{employeeBean.minSalaryFilter} #{employeeBean.maxSalaryFilter}
label Minimum salary Maximum salary
id minSalaryFilter maxSalaryFilter
f:validator binding #{bindings.Salary.validator} #{bindings.Salary.validator}
af:convertNumber groupingUsed false false
af:convertNumber pattern #{bindings.Salary.format} #{bindings.Salary.format}


The af:commandButton has the following properties:

Property Value
text Filter
id filter
actionListener #{employeeBean.filter}


Add to the page definition of the table page inside the bindings tag:

<attributeValues IterBinding="EmployeesViewIterator"

id="Salary">

<AttrNames>

<Item Value="Salary"/>

</AttrNames>

</attributeValues>

Set the partialTrigger property of the table:

partialTriggers="::filter"


Employee bean



In the table page references are made to the employeeBean. This bean class is defined in the employee task flow:

Managed bean property Value
Name employeeBean
Class nl.hr.demo.view.beans.EmployeeBean
Scope Request


The bean class contains two class variables RichInputText minSalaryFilter and RichInputText maxSalaryFilter and their accessors.

The bean class also contains the actionListener implementation. In this implementation the EmployeesView is retrieved from the application module and the values of minSalaryFilter and maxSalaryFilter are copied to the EmployeesView and the view is queried:

public void filter (ActionEvent actionEvent) {

EmployeesViewImpl view = getService().getEmployeesView();

view.setb_min_salary(getValue(minSalaryFilter));

view.setb_max_salary(getValue(maxSalaryFilter));

view.executeQuery();

}



private Number getMValue(RichInputText item) {

if (item == null || item.getValue() == null) {

return null;

}

return new Number(((BigDecimal) item.getValue()).intValue());

}



private static HrAppModuleImpl getService() {

DCBindingContainer bc = (DCBindingContainer)FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{bindings}", BindingContainer.class);

DCDataControl dc = bc.findDataControl("HrAppModuleDataControl");

return (HrAppModuleImpl) dc.getDataProvider();

}

The class uses the next imported classes:

Class name Package
BigDecimal java.math
FacesContext javax.faces.context
ActionEvent javax.faces.event
DCBindingContainer oracle.adf.model.binding
DCDataControl oracle.adf.model.binding
RichInputText oracle.adf.view.rich.component.rich.input
BindingContainer oracle.binding
Number oracle.jbo.domain

Fix the table width



Now we’re ready to fix the table width with a generic solution. For this solution we need:
  • A bean class that binds the table and calculates the width (in or exclusive scrollbar)
  • Set properties in the table to use the bean class

Bean class



Define in the unbounded task flow a new bean:

Managed bean property Value
Name tableBean
Class nl.hr.demo.view.util.TableBean
Scope Request


The bean class contains a class variables RichTable table and its accessors.

The bean class contains 1 method which returns the width of the table as an int. The width is calculated by:
  • Loop over all columns of the table:
    • If the column is rendered and visible then add its width to a local integer and add 1 to another local integer that counts the number of columns.
  • Add to the width the number of columns multiplied by 5.
  • Retrieve the number of rows in the view object that is displayed by the table:
    • Get the value property of the table cast it to a CollectionModel and get the estimatedRowCount value.
  • If the number of rows is bigger than the number of rows displayed in the table (the autoHeightRows property of the table) then add 16 to the width.
  • Return the width.

public int getWidth() {

if (table == null) {

return 600;

}

try {

int width = 0;

int columns = 0;

List list = table.getChildren();

for (int i = 0; i < list.size(); i++) {

UIComponent component = list.get(i);

if (component instanceof RichColumn) {

RichColumn column = (RichColumn) component;

if (column.isRendered() && column.isVisible()) {

width += new Integer(column.getWidth()).intValue();

columns++;

}

}

}

width += (columns * 5);

int nrRows = 0;

if (table.getValue() != null) {

CollectionModel tableData = (CollectionModel) table.getValue();

nrRows = tableData.getEstimatedRowCount();

}

if (nrRows > table.getAutoHeightRows()) {

width += 16;

}

return width;

} catch (Exception e) {

e.printStackTrace();

return 600;

}

}

The class uses the next imported classes:

Class name Package
List java.util
UIComponent javax.faces.component
RichColumn oracle.adf.view.rich.component.rich.data
RichTable oracle.adf.view.rich.component.rich.data
CollectionModel org.apache.myfaces.trinidad.model

Table properties



Set the following properties of the employees table:

Property Value Description
binding #{tableBean.table} Binds the table to the RichTable class variable of the TableBean
width #{tableBean.width} The width is set to the result of the getWidth method of the TableBean
autoHeightRows 10 The (maximum) number of rows displayed in the screen
contentDelivery immediate Must be set to immediate otherwise the autoHeightRows property doesn’t work


Note: Firefox displays one row more than set in the autoHeightRows property . When the autoHeightRows property is set to 10 and there 11 rows in the view object the table bean adds space for a scrollbar but because firefox displays 1 row more the scrollbar space is empty.

Too avoid this a small correction should be made to the TableBean (only tested in IE and Firefox):

Change:

if (nrRows > table.getAutoHeightRows()) {

To:

if (nrRows > (table.getAutoHeightRows() + browserCorrectionRowsDisplayed())) {

The implementation of the browserCorrectionRowsDisplayed method:

public int browserCorrectionRowsDisplayed() {

String browser = (RequestContext.getCurrentInstance()).getAgent().getAgentName();

if (browser != null && browser.equalsIgnoreCase("gecko")) {

return 1;

}

return 0;

}

The result:

woensdag 23 februari 2011

How to fix menu item navigation when using bounded task-flows

Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES, DEPARTMENTS


Summary



One of the strength of ADF11 compared to 10 are the task-flows. Task flows increase reusability of parts of applications. But when you want to use an overall menu bar that should be accessible from any place in the applications and you use bounded task flows then some extra code is required to make the menu work from any place in the application.

In this blog a solution is provided to make the menu work when using bounded (and unbounded) taskflows.

Overview of the page flow:



Setup example application



For this blog an example application is created based on the HR schema. The example application contains an employees table width detail form and a departments table and detail form. The detail forms are created in a separate page. For the employees table and form a bounded task flow is created this bounded task flow is started from the menu. For the departments table and form another bounded task flow is created which is also started from the menu.

Model layer



Create the following entities:
Entity name Based on table of HR schema Customizations made
Employee EMPLOYEES None
Department DEPARTMENTS None


Create the following view objects:
View object name Based on entities Customizations made
EmployeesView Employee None
DepartmentsView Department None


Create an application module HrAppModule which exposes the EmployeesView and DepartmentsView.



Task flow




Unbounded task flow



The unbounded task flow form where we start with the solution looks like this:



There are no customizations made, the task flow is created by drag and drop.

Bounded task flow



The employees task flow and the departments task flow are bounded task flows. Both look like this (but for departments the view is DepartmentsTable and DepartmentForm).



The next properties are set (for both bounded task flows):
Property Value
usePageFragments false

Share data controls with calling task flow true




Home page



An empty page only containing the menu is created.

Menu



The menu is a JSFF page that’s included in all pages (or in our case in the page template).
The menu contains:
  • Menu: Employees
    • Command menu item: Maintain employees with action startEmployees and immediate true.
  • Menu: Departments
    • Command menu item: Maintain departments with action startDepartments and immediate true.
  • Menu: Help
    • Command menu item: About with a showPopupBehaviour for an OK popup displaying ‘This a demo HR application.’

Table page



The table pages are created by drag and drop from the Data Controls. The table is dropped as ADF Read-only table with Row Selection and Sorting checked, all columns are displayed.





The Row Selection property must be checked, this causes the following properties to be set in the table:
PropertyValue
selectedRowKeys#{bindings.EmployeesView.collectionModel.selectedRow}
selectionListener#{bindings.EmployeesView.collectionModel.makeCurrent}
rowSelectionsingle


Although in the JSPX page the selectedRowKeys and selectionListener statements contains warnings that the references methods cannot be found they can be found runtime.

Add the end of the table another column is added. This column contains a commandLink that triggers the edit navigation to the form page:

<af:column headerText="Edit employee"

id="editColumn">

<af:commandLink text="Edit"

id="edit"

action="edit"/>

</af:column>

Underneath the table a Cancel button (af:commandButton) is added which ends the task flow.
af:commandButton propertyValue
textCancel
idcancel
actioncancel
immediatetrue


The DepartmentsTable is created in the same way but then for the DepartmentsView.

Form page



The form pages are created by drag and drop from the Data Controls. The form is dropped as ADF Form all attributes are displayed.



To navigate back from the form to the table a rollback button (af:commandButton) is added by drag and drop the Rollback operation from the datacontrol palette as a button.



The submit button is created in the same way but then from the Commit operation. The DepartmentForm is created in the same way but then for the DepartmentsView.

Fix the menu navigation



When the application described above is deployed the navigation from the menu only works when the user is in the Home page (an empty page containing only the menu). When the user opens the Employees table page (from the Employees menu) and then tries to open the Departments table page (from the Departments menu) it doesn’t work. The user can navigate to this page using the cancel button in the Employees table page which navigates back to the home page and then start the departments table.

The reason the menu doesn’t work is that when the Employees table is opened the user is in the employees-task-flow. In this task-flow the Departments menu action (startDepartments) is not defined so it cannot be found.

Now we know what causes the issue we can correct it:
  • When a menu item action is returned:/li>
    • Check if the user is currently in a bounded task flow
    • If so quit this task flow (with a task flow return activity)
    • Then retry to execute the action

Menu


First we need to know if a menu item action is returned or not.
For all menu items (that do not trigger a popup), change the action precede all returned values by a fix string for example menu_.
So the Employees menu containing the commandMenuItem Maintain employees triggered the action startEmployees, this is changed in menu_startEmployees.

Change action of the menu item, add ‘menu_’ in front of the action string.

Note: the action results defined in the unbounded task flow should not be changed!

Navigation handler


A custom navigation handler class is created. The handleNavigation method of this class is used for all navigations in the application.
Create a class:
Class name Extends
CustomNavigationHandlerImpl oracle.adfinternal.controller.application.NavigationHandlerImpl

To let the application use this class it should be defined in the faces-congfig.xml, this can be done in the overview tab option Application:

In the CustomNavigationHandlerImpl the handleNavigation is overridden. In the outcome parameter of this method the resulting string of the action that is called is passed. If this outcome starts with menu_ (this should equal the string that was added in front of the original action of the menu page) then a menu item was triggered. If so we need to check whether the user is currently in a bounded task flow or not, this is done by the abandonTaskFlowIfNeeded method.

public void handleNavigation(FacesContext facesContext, String action, String outcome) {
if (outcome != null && outcome.startsWith("menu_")) {
abandonTaskFlowIfNeeded(facesContext, action, outcome);
} else {
super.handleNavigation(facesContext, action, outcome);
}
}

The abandonTaskFlowIfNeeded method check whether the user is currently in a bounded task flow, if this is not the case then just call back to handleNavigation but pass instead of the original outcome the outcome without menu_. If the user is in a bounded task flow then store the outcome without menu_ on the request (with name triggeredMenuItem) and execute the action “abondonTaskflow” instead of the called menu item action.

public void abandonTaskFlowIfNeeded(FacesContext facesContext,String action, String outcome) {
String strippedOutcome = outcome.substring("menu_".length());
TaskFlowContext tfc = ControllerContext.getInstance().getCurrentViewPort().getTaskFlowContext();
if (tfc.getTaskFlowId() == null) {
handleNavigation(facesContext, action, strippedOutcome);
} else {
Map requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
requestMap.put("triggeredMenuItem", strippedOutcome);
handleNavigation(facesContext, null, "abandonTaskflow");
}
}


Bounded task flow


Now when the user is in a bounded task flow and triggers a menu item to open the action abandonTaskflow is executed instead of the menu item action. This action must be defined in the bounded task flow.
Because the same solution holds for all bounded task flows used in the application we create a task flow template:

The following properties are set on the ExecuteMenuCommand Task Flow Return activity:
Property Value
Outcome startMenuItem

All bounded task flows (in this example the employees-task-flow and departments-task-flow) must be based on this template.

The Task Flow Return activity has outcome startMenuItem, this outcome is passed to the unbounded taskflow. When this action is returned there is parameter triggeredMenuItem on the request. This parameter contains the original menu action that was triggered.

Unbounded task flow


Define a method call in the unbounded task flow for the outcome startMenuItem.

The following properties are set on the StartMenuItemMethod Method Call activity:
Property Value
Method #{customRouter.getMenuItemFromRequest}
toString true
Parameters Class: java.lang.String

Value: #{requestScope.triggeredMenuItem}

The customRouter refers to a managed bean defined in the unbounded task flow:
Managed bean property Value
Name customRouter
Class nl.hr.demo.view.util.CustomRouter
Scope Request

This bean class contains 1 method, getMenuItemFromRequest that returns the value passed as a parameter (which is filled this the triggeredMenuItem value on the request filled by the CustomNavigationHandlerImpl class):

public String getMenuItemFromRequest(String request) {
return request;
}

Now the menu items work whether we’re in a bounded task flow or not.

maandag 21 februari 2011

How to trigger an action on double click of an ADF table row

Technology: ADF11g
Developed in: JDeveloper 11.1.1.3.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES


Summary



In this blog a solution is provided to execute custom code and / or navigation when a user double clicks on a row of an ADF table.

In this example only navigation is executed. A form page is opened for the selected employee in the table.

Overview from page flow:



Model layer



Create an Employee entity object based on the EMPLOYEES table of the HR schema. No customizations are needed for this example.

Create an EmployeesView view object based on the Employee entity. No customizations are mode to this view object.

Create an application module HrAppModule which exposes the EmployeesView.



Task flow



A bounded task flow is created for this example:



The next properties are set:
Property Value
usePageFragments false

Share data controls with calling task flow true



The application also contains an unbounded task flow:



This task flow only starts the bounded task flow and it contains a managed bean:

Managed bean property Value
Name hrBean
Class nl.hr.demo.view.HrBean
Scope Request


This bean class is used in this example to handle the navigation.

EmployeesTable page



The table page is created by drag and drop from the Data Controls. The table is dropped as ADF Read-only table with Row Selection and Sorting checked, all columns are displayed.





The Row Selection property must be checked, this causes the following properties to be set in the table:
Property Value
selectedRowKeys #{bindings.EmployeesView.collectionModel.selectedRow}
selectionListener #{bindings.EmployeesView.collectionModel.makeCurrent}
rowSelection single


Although in the JSPX page the selectedRowKeys and selectionListener statements contains warnings that the references methods cannot be found they can be found runtime.

Underneath the table a Cancel button (af:commandButton) is added which ends the task flow.

af:commandButton property Value
text Cancel
id cancel
action cancel
immediate true

Handle double click



To handle a double click event we need:
  • A listener for double click on the table
  • Javascript method that listens to the double click and sends it to the server
  • Java bean method to execute custom logic or navigation

A listener for double click on the table



The listener is added in the af:table tag (just before </af:table>). A client listener is added which triggers the javascript method and a server listener is added which is triggered from the javascript method and triggers the java bean method.

The af:clientListener contains:
af:clientListener property Value Meaning
type dblClick Executed when a row is double clicked
method handleTableDoubleClick Execute the javascipt method with this name


The af: serverListener contains:

af:serverListener property Value Meaning
type TableDoubleClickEvent The name of the event triggered by the javascript method
method #{hrBean.select} Execute the java bean method select in the HrBean class

Javascript method that listens to the double click and sends it to the server



First a javascript file must be added to the page so it can be found runtime.

Our javascript file is called hr and it’s located in a javaScript folder under public_html.

When you’re not using a page template then the java script reference can be added to the page immediate after the <af:form> tag. When you do use a page reference add the java script reference immediate after the <jsp:directive.page> tag.

<af:resource type="javascript" source="/javaScript/hr.js"/>

The handleTableDoubleClick method:

function handleTableDoubleClick(event){
var table = event.getSource();
AdfCustomEvent.queue(table, "TableDoubleClickEvent",{}, true);
event.cancel();
}

When the method is called an AdfCustomEvent is triggered for the table that started the clientListener method with name TableDoubleClickEvent (this must equal the type of the serverListener!) the last property ‘true’ is the immediate clause, so whether validations should be executed (false) or not (true).

Java bean method to execute custom logic or navigation



The HrBean class contains a method select which is triggered by the serverListener. In this method navigation is added to the EmployeeForm. This is done by executing the handleNavigation method of the NavigationHandler class, the last parameter of this method should equal the control-flow-case from outcome parameter as defined in the employees-task-flow.

public void select(ClientEvent clientEvent) {
NavigationHandler nh = FacesContext.getCurrentInstance().getApplication().getNavigationHandler();
nh.handleNavigation(FacesContext.getCurrentInstance(), "", "edit");
}

Class Import
NavigationHandler javax.faces.application.NavigationHandler
ClientEvent oracle.adf.view.rich.render.ClientEvent
FacesContext javax.faces.context.FacesContext

EmployeeForm page



The form page is created by drag and drop from the Data Controls. The form is dropped as ADF Form all attributes are displayed.



To navigate back from the form to the table a rollback button (af:commandButton) is added by drag and drop the Rollback operation from the datacontrol palette as a button.



The submit button is created in the same way but then from the Commit operation.