JDeveloper: Advanced Skin Technique

This post is about an advanced technique to change the look and feel of an ADF application. Changes to the look & feel are normally done via a skin which you use to change descriptors which are used by the ADF components. The general technique to do this is described in many blogs and articles like ADF Faces Skin Editor – How to Work with It and the official documentation at Oracle ADF Skin Editor.

In this blog we look at an advanced technique which helps to change the look and feel of components like af:query and pf:panelCollection which you can’t change using the normal available descriptors. In the below image you see the Skin Editor showing the ADF components skin descriptors.

selection_985

Use Case

In this use case we work with the af:panelCollection component. This component is used to wrap af:tree, af:treeTable and af:table components to provide additional functions. From the documentation of af:panelCollection

A panel component that aggregates collection components like table, treeTable and tree to display standard/application menus, toolbars and statusbar items.

The default top level menu and toolbar items vary depending on the component used as the child of the panelCollection.

  • For table, tree and treeTable, the default top level menu item is View.
  • For table and treeTable with selectable columns, the default top level menu items are View and Format.
  • For table and treeTable, the default toolbar item is Detach.
  • For table and treeTable with selectable columns, the default top level toolbar items are Freeze, Detach and Wrap.
  • For tree and treeTable, if the pathStamp facet is used, the toolbar buttons Go Up, Go To Top, Show as Top also appear.

The component allows us to switch off some function

Value Turns off
statusBar Status bar
viewMenu ‘View’ menu
formatMenu ‘Format’ menu
columnsMenuItem ‘Columns’ sub-menu item
columnsMenuItem:col1,col20 Columns with column ID: ‘col1’ and ‘col20’ inside ‘Columns’ sub-menu
freezeMenuItem ‘Freeze’ menu item
detachMenuItem ‘Detach’ menu item
sortMenuItem ‘Sort’ menu item
reorderColumnsMenuItem ‘Reorder Columns’ menu item
resizeColumnsMenuItem ‘Resize Columns’ menu item
wrapMenuItem ‘Wrap’ menu item
showAsTopMenuItem Tree/TreeTable ‘Show As Top’ menu item
scrollToFirstMenuItem Tree/TreeTable ‘Scroll To First’ menu item
scrollToLastMenuItem Tree/TreeTable ‘Scroll To Last’ menu item
freezeToolbarItem ‘Freeze’ toolbar item
detachToolbarItem ‘Detach’ toolbar item
wrapToolbarItem ‘Wrap’ toolbar item
showAsTopToolbarItem Tree/TreeTable ‘Show As Top’ toolbar item
wrap ‘Wrap’ menu and toolbar items
freeze ‘Freeze’ menu and toolbar items
detach ‘Detach’ menu and toolbar items

As a sample the image below shows a normal af:panelCollection (upper half) and an af:panelCollection with the view menu and the toolbar switched off (lower half)

selection_979

Looking at the possible things to switch off we don’t see anything to switch off the ‘Query by Example’ (QBE) icon. There is no feature toggle to turn this function on or off. An easy way to get rid of the icon would be to make the table not filterable. However, if we like the table to be filterable but don’t want to show the icon to switch the feature off, we have to use an advanced skin technique.

What can we do to get rid of the icon in the tool bar?

The idea is to use a skin or special css to hide the icon or the container which holds the icon. To find the container we first inspect the page in the browser using the browsers ‘Developer Tools’ which you can reach by hitting F12 in your browser. Below you see Chrome 55 with activated ‘Developer Tools’

Selection_211.jpg

The image shows the toolbars QBE image as selected element on the page (left red rectangle) and the style classes which are in use for this element (right red rectangle). The names ‘.xfo’ and ‘.xfr’ are the names of the style classes. They are minimized to reduce the download size of the page, but they are not ‘readable’. 

The first thing to do is to make the names ‘readable’ for us. We need to know which skin selector generated the style class. For this we set a context parameter in the web.xml file

 <context-param>
 <param-name>org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION</param-name>
 <param-value>true</param-value>
 </context-param>

Setting this parameter to true will show us the clear names. The image below shows the same selection only this time with the real names

selection_212

One other nice feature of the ‘Developer Tools’ is that you can inspect elements by just hover over them on the page. This allow us to easily find the element we want to hide via css. Click on the icon marked in hte below image

selection_213

and move the mouse cursor over the page. You see the HTML and the active styles of the element under the cursor. This feature we use to find an element which holds the icon we want to hide and which we can address via css .

selection_214

CSS allows us to address elements inside a skin selector.  For this you need to know the skin selector, the tag or container and it’s ID inside the selector you want to address. In the image above we see the ID of the icon container we want to hide as “id=’pc1:_qbeTbr'” and the container or tag itself which is a ‘div’. The skin selector is the af|panelCollection. With this information we can can change the style attached to the ‘div’ container with the id ‘*_qbeTbr’  in the af|panelCollection as

af|panelCollection div[id$='_qbeTbr'] {
    display: none;
}

This we can add to our skin.css file. However, if we just add it this way it’s changing all af:panelCollection in our application.  If we want this only to be active for specific af:panelColletion we can add a style class name like

af|panelCollection.myPCClass div[id$='_qbeTbr'] {
    display: none;
}

Now we can add the stale class name ‘myPCClass’ to the af:panelCollection when we like the QBE icon not to be shown

 <af:panelCollection id="pc1" styleClass="myPCClass">
   <f:facet name="menus"/>
   <f:facet name="toolbar"/>
   <af:table value="#{bindings.EmployeesView1.collectionModel}" ...
   ...
 <af:panelCollection id="pc2" featuresOff="detachToolbarItem viewMenu">
 <f:facet name="menus"/>
 <f:facet name="toolbar"/>
 ...

will generate this UI output

selection_217

 

As we see, the QBE icon is gone. In the original page we have placed two af:panelCollection components. As you added the new style class only to one of them, the other QBE icon is still visible.

Extending

You can use hte same technique for other complex ADF components like af:query. Here you can style the save button which normally not  supported.

Download

You can download the sample which is build using JDev 12.2.1.2.0  and uses the HR DB schema from GitHub BlogAdvancedSkin

Summary of Day 5 at Oracle Open World 2016

I started the day with a session on Alta UI ‘Implementing Oracle’s New Alta UI Features’ by Richard Wright. Richard started by giving some reasoning about why Oracle developed Alta UI. It was manly because the users demanded a more mobile friendly UI. The biggest change which came with Alta UI was that the UI has to be build by thinking ‘mobile first’ and by more designing the flow of operations by personas. Only then you gain the full advantage of the Alta UI.

Transforming a older (legacy) application to a modern application using the Alta UI is not just migrating the skin. You have to redo the UI and design it for mobile first.This means that you have to think about different device sizes which in the end means that you have to design the application in a responsive manner.

Here the page stretches on the device. This is mostly not working on small devices as it makes the user to zoom into the right section to see the information. Because of hte size mobile friendly means that you try to visualize the information instead of e.g. showing the user a table. An image is giving information a human can intake more easily than  data in a table.

For a developer this means that using a list view should be preferred over using a table. A list view allows better responsive design.

Summary is that you should

  • Leverage major UI updates as an opportunity
  • Verify actual users versus previously targeted users
  • Target UI for preferred user devices
  • Understand their most important artifacts and tasks

Next session of the day was ‘Cloud-Native Application Development with Oracle Application Container Cloud‘ by Shaun Smith, Anand Kothari and Eric Jacobsen. This session is about the Oracle Application Container Cloud Service which lets you run native Java SE applications or Node.js based application run in the cloud.

I already mentioned the ‘Cloud Native Architecture’ on day 2.

img_bqnk2w

 

and the demands on the application development

and tools to use to make this architecture work from Oracles point of view

The Application Container Cloud should allow you to make such development simpley by

  • Develop
  • Zip
  • Deploy

your application. This can be done on a polyglot platform using java, php, Node.js and later even Ruby and Java EE. It’s an open platform allowing you to run many applications. Oracle provides a Linux system and you can bring what ever you like.

All this runs on Docker containers. The only constraint is that the applications must be stateless, as the containers are build up and shut down on the fly to load balance your application. This is done automatically without you needing to interfere.

Once your application runs monitoring the JVM or the performance of the application is done via the cloud services. Patching, if needed, is done for you too. Not that you don’t know about it, but it’s just a click on a button. If you don’t like the patch because it breaks your application you can easily rollback the patch


Final session of the day and OOW 2016 for me was ‘Using Docker with Continuous Delivery in Oracle Cloud‘ by Greg Stachnick and Mike Raab. This session talked about how Docker is used in the Oracle Container Cloud Service to allow agile, containerized development in the cloud.

The first part was about the developers cloud which was covered in almost every session about the cloud.

img_20160922_132523

Second part was about the Container Cloud Service and it’s base implementation StackEngine (a company bought by Oracle end of last year).

IMG_20160922_132704.jpg

Key features of the Container Cloud are shown in the image below:

IMG_20160922_132757.jpg

When setting up a service in a docker container the UI looks like

IMG_20160922_134200.jpg

Changes made in the UI are reflected directly in a docker run script (which you can get on the same page). Spinning up a new container is a matter of two clicks:

Stacks are the equivalent to Dockers composer but have some add on like you are able to add parameters to the containers.

In the end the Container Cloud service is a flexible ‘bring your own’ container and run it in hte cloud. Don’t forget to bring the needed licences too 🙂

Product will be available within the next 12 month!


That was the OOW2016 for me. See you next year!

 

 

dvt:treemap showing node detail in popup

This post describes how to implement an dvt:treemap which shows a af:popup when the user clicks on a detail node in the map.
The documentation of the dvt:treemap component tell us that the dvt:treemapnode supports the af:showPopupBehaviortag and reacts on the ‘click’ and ‘mouseHover’ events.
This is part of the solution and allows us to begin implementing the use case. We add an af:showPopupBehavior to the nodes we want to show detail information for.

After creating a default Fusion Web Application which uses the HR DB schema, we begin with creating the data model for the model project. For this small sample the departments and employees tables will be sufficient.


The views are named according to their usage to make it easier to understand the model. This is all we need for the model.

Let’s start with the UI which only consist of a single page. The page has a header part and a center part. In the center area we build the treemap by dragging the Departments from the data controls onto the page and dropping it as treemap. After that, in the dialog we specify the first level of the map to be the departmentId (which shows the department name as the label) and the for the second level we choose the employeeId (which shows the last name of the employee as label) from the employees. The whole process is shown in the gallery below.


The resulting treemap is very basic in it’s features, e.g. there is no legend as you see later.
In the next step we create an af:popup to show the nodes detail information. This process is outlined in the next gallery. We drag the popup component onto the page below the af:treemap component

One thing to take note of are the properties of the popup. First we set the content delivery to ‘lazyUncached’, which makes sure that the data is loaded every time the popup is opened. Otherwise we’ll see only the data from the first time the popup has been opened. Second change is to set the launcherVar to ‘source’. This is the variable name we later use to access the node data. Third change is to set the event context to ‘launcher’. This means that events delivered by the popup and its descendents are delivered in the context of the launch source.

The treemap for example, when an event is delivered ‘in context’ then the data for the node clicked is made ‘current’ before the event listener is called, so if getRowData() is called on the collectionModel in the event listener it will return the data of the node that triggered the event. This is exactly what we need.

Finally we add a popupFetchListener to the popup which we use to get the data from the current node to a variable in the bindings. In the sample this variable ‘nodeInfo’ is defined in the variable iterator of the page and an attribute binding ‘nodeInfo1’ is added. More info on this can be found here.


The code below shows the popupFetchListener:

package de.hahn.blog.treemappopup.view.beans;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;

import oracle.adf.model.BindingContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.view.rich.event.PopupFetchEvent;

import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;


/**
 * Treemap handler bean
 * @author Timo Hahn
 */
public class TreemapBean {
    private static ADFLogger logger = ADFLogger.createADFLogger(TreemapBean.class);

    public TreemapBean() {
    }

    /**
     * listen to popup fetch.
     * @param popupFetchEvent event triggerd the fetch
     */
    public void fetchListener(PopupFetchEvent popupFetchEvent) {
        // retrieve node information 
        String lastName = (String) getValueFromExpression("#{source.currentRowData.lastName}");
        Integer id = (Integer) getValueFromExpression("#{source.currentRowData.EmployeeId}");
        //build info string
        String res = lastName + " id: " + id;
        logger.info("Information: " + res);
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("nodeInfo1");
        //set the value to it
        attr.setInputValue(res);
    }

    // get a value as object from an expression
    private Object getValueFromExpression(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        Application app = facesCtx.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesCtx.getELContext();
        Object obj = elFactory.createValueExpression(elContext, name, Object.class).getValue(elContext);
        return obj;
    }
}

Finally we have to design the popup to show the node info from the attribute binding ‘nodeInfo1’. The popup uses a dialog with an af:outputText like

Show the node info in the popup

Show the node info in the popup


and set an af:showPopupBehavior to the node showing the employees

Running the finished application brings up the treemap, not pretty but enough to see this use case working. If we click on an employee node we see the popup with the last name of the employee and the employee id, the primary key of the selected row in the employees iterator.

You can download the sample application which was build using JDeveloper 12.1.3 and the HR DB schema from GitHub.

How-to filter ADF bound tables by date range (JDeveloper 12.1.x)

Based on an older article from Frank Nimphius How-to filter ADF bound tables by date range JDeveloper 11.1.1.4 I got a interesting question on the OTN JDeveloper & ADF forum why the solution provided in the article does not work in JDev 12c.

The solution from Frank’s article is designed for JDev 11.1.1.4.0. Today’s version of JDev is 12.1.3 where the solution does not seem to work. Migrating the source of the article and running it under JDev 12.1.3 indeed shows, that filtering the employees records for a date range does not work at all. Setting dates into the filter and hitting enter to activate the filter does not filter the data in the table.
The reason for this was easily found by debugging the code. Set a breakpoint into the query listener which is setup in the table

<af:table value="#{bindings.allEmployees.collectionModel}" var="row" 
  rows="#{bindings.allEmployees.rangeSize}"
  emptyText="#{bindings.allEmployees.viewable ? 'No data to display.' : 'Access Denied.'}"
  fetchSize="#{bindings.allEmployees.rangeSize}" rowBandingInterval="0"
  filterModel="#{bindings.allEmployeesQuery.queryDescriptor}" filterVisible="true" 
  varStatus="vs" selectedRowKeys="#{bindings.allEmployees.collectionModel.selectedRow}"
  selectionListener="#{bindings.allEmployees.collectionModel.makeCurrent}" 
  rowSelection="single" id="t1" styleClass="AFStretchWidth"  partialTriggers="::cb1"
  queryListener="#{EmployeeQueryBean.onEmployeeQuery}">

As you can see it’s pointing to a bean method ‘onEmplyoeeQuery’. A look into this method reveals that the method FilterableQueryDescriptor.getFilterCriteria() has been deprecated.

        FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();
        Map map = fqd.getFilterCriteria();

Instead of the deprecated method you should use the method FilterableQueryDescriptor.getFilterConjunctionCriterion() which now holds the map of parameters.

        FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();
        ConjunctionCriterion cc = fqd.getFilterConjunctionCriterion();
        Map<String, Criterion> criterionMap = cc.getCriterionMap();

When you set a breakpoint in this method and step through the code you see that the values entered into the filter fields in the UI are not visible in the map as Frank describes in his article.

Criterion Map and old FilterCriteria Map

Criterion Map and old FilterCriteria Map


As you can see there are no map entries for the made up variables ‘HireStartRange’ and ‘HireEndRange’. This is the reason the filter by date range does not work. There are simply not dates to filter the rows.

I’m not sure if this is a bug or a change in behavior which was made for a reason. Anyway, you can’t just simply add values to the map anymore.

The solution to fix the problem is simple. As you can’t store additional values in the criterion map, you have to store the values entered by the user somewhere else. A valid storage area is the variables iterator each pagedef holds.
In one of my other blogs Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef I showed how to add temporary variables in this iterator.

Create two new variables inside the variable iterator of type oracle.jbo.domain.Date, name them ‘startDate’ and ‘endDate’. Then create attribute bindings for them.
The final touch is to wire the new variables up in the HireDate filter for start range and end range:

                                    <af:column sortProperty="HireDate" filterable="true" sortable="true"
                                               headerText="#{bindings.allEmployees.hints.HireDate.label}" id="c1" width="277">
                                        <f:facet name="filter">
                                            <af:panelGroupLayout id="pgl2" layout="horizontal">
                                                <af:panelLabelAndMessage label="From: " id="plam1">
                                                    <af:inputDate id="id2" value="#{bindings.startDate1.inputValue}" clientComponent="false">
                                                        <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                                        <f:validator binding="#{bindings.HireDate.validator}"/>
                                                    </af:inputDate>
                                                </af:panelLabelAndMessage>
                                                <af:spacer width="5" height="5" id="s1"/>
                                                <af:panelLabelAndMessage label="To:" id="plam2">
                                                    <af:inputDate id="id3" value="#{bindings.endDate1.inputValue}" required="false" clientComponent="false">
                                                        <f:validator binding="#{bindings.HireDate.validator}"/>
                                                        <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                                    </af:inputDate>
                                                </af:panelLabelAndMessage>
                                            </af:panelGroupLayout>
                                        </f:facet>
                                        <af:inputDate value="#{row.bindings.HireDate.inputValue}" label="#{bindings.allEmployees.hints.HireDate.label}"
                                                      required="#{bindings.allEmployees.hints.HireDate.mandatory}"
                                                      shortDesc="#{bindings.allEmployees.hints.HireDate.tooltip}" id="id1" styleClass="AFStretchWidth">
                                            <f:validator binding="#{row.bindings.HireDate.validator}"/>
                                            <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                        </af:inputDate>
                                    </af:column>

The code above shows the new column for the HireDate and the new storage location for the startDateRange as ‘value=”#{bindings.startDate1.inputValue}”‘ and EndDateRange as ‘value=”#{bindings.endDate1.inputValue}”‘. Next we change the bean method which reads the filter values and calls the query:

    public void onEmployeeQuery(QueryEvent queryEvent) {
        //default EL string created when dragging the table
        //to the JSF page
        //#{bindings.allEmployeesQuery.processQuery}

        BindingContext bctx = BindingContext.getCurrent();
        DCBindingContainer bindings = (DCBindingContainer) bctx.getCurrentBindingsEntry();

        //access the method bindings to set the bind variables on the ViewCriteria
        OperationBinding rangeStartOperationBinding = bindings.getOperationBinding("setHireDateRangeStart");
        OperationBinding rangeEndOperationBinding = bindings.getOperationBinding("setHireDateRangeEnd");

        // get the start date and end date from the temporary valiables
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("startDate1");
        oracle.jbo.domain.Date sd = (oracle.jbo.domain.Date) attr.getInputValue();
        attr = (AttributeBinding) bindings.getControlBinding("endDate1");
        oracle.jbo.domain.Date ed = (oracle.jbo.domain.Date) attr.getInputValue();

        //set the start and end date of the range to search
        rangeStartOperationBinding.getParamsMap().put("value", sd);
        rangeEndOperationBinding.getParamsMap().put("value", ed);

        //set bind variable on the business service
        rangeStartOperationBinding.execute();
        rangeEndOperationBinding.execute();

        invokeMethodExpression("#{bindings.allEmployeesQuery.processQuery}", Object.class, QueryEvent.class, queryEvent);
    }

In line 14-17 you see that we read the values from the newly created attribute bindings for the temporary variables. After removing the unnecessary parts of the code, which tried to read the values from the map, the rest of the code remains as is.

Here is an image of the now working filter by date range

Filter Table by Date Range

Filter Table by Date Range

Please note that if you run the sample in your environment, that you have to change the DB connection to the HR DB schema according to your environment. You can download the changed code for the sample from GitHub

JDev 12.1.3: Use Default Activity Instead of the Deprecated Invoke Action

Since JDeveloper 12.1.3 the invoke action used in earlier version has been deprecated. Users still using the old invoke action to load data on page load should migrate their code to using the default activity in a bounded task flow instead. This article describes how to use the executeWithParams method as a default activity in a bounded task flow (btf) to load data to be shown in a region. For this we implement a common

Use Case:
in a text field the user enters a string which should be used to look-up data in the DB and show the data as a table in a region.
For this we use the HR schema and build a look-up for locations after the name of the city of the location. In a page the user can insert the name or part of a cities name into a text field. This input is send as parameter to a bounded task flow. The default activity of the btf calls a method in the view object which uses a view criteria to search for cities starting with the given input data. In a second implementation the same technique is used but a where clause is used in the VO and the VO is called with executeWithParams. The result of the search is displayed as a table in a region.

Implementation

Model Project:
We start by creating a new ‘Fusion Web Application’ and creating a model project of the HR DB schema. Here we only use the location table for which we create entity object and view object.
Now we create the view criteria which we use to find locations by part of the city name.

Next step is to create the java class for the view object including the method to safely access the created bind variable. In the class we add a method to apply the created view criteria which we expose in the client interface well as the methods to access bind variables.


Finally we have to make sure that the locations view object is part of the data model of the application module.
Resulting Application Module Data Model

Resulting Application Module Data Model


Next we add another view object to the data model which we use to implement the use case a second time. This time we use the view criteria we defined in the view object LocationsView and select it as the default where clause.

ViewController Project:
We start implementing the view controller project by first adding a start page, ‘Start’, to the unbounded task flow in adfc-config.xml. For this page we use a quick layout (One Column, Header stretched).

After opening the page (which creates it) we add a third grid row to the panelGridLayout we got from the quick layout which later holds the result table. In the first grid row we add a captain for the page, ‘Execute with param sample’, the second grid row we add an af:inputText which holds the users input for the city name to search for.
The page looks like

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="Start.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="ExecuteWithParams Test" id="ot1" inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:inputText label="City" id="it1" value="" autoSubmit="true"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow id="gr3">
                    <af:gridCell id="gc3">
                        <!-- REGION HERE -->
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Now we create a pageDefinition for the page, where we define a variable and an attribute binding which holds the users input into the inputText we added to a grid row below the header.


The final inputText look like

<af:inputText label="City" id="it1" value="#{bindings.searchCityName1.inputValue}" autoSubmit="true"/>

As you see we set the autoSubmit property to true as we don’t have (and need) a button to submit the data to the binding layer.

The next task is to create a new bounded task flow which has one input parameter, which is used to search for locations with cities starting with the given parameter from the inputText component.

Once the bounded task flow is created we can drag this btf onto the start page and drop it in the girdCell in the third gridRow and wire the parameter for the task flow to the value we have stored in the in the variable iterator via the inputText.

Finally we make the region refresh whenever the inputParamter of the task flow changes by setting the regions refresh property to ‘ifNeeded’.
The final ‘Start’ page layout looks like

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="Start.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="ExecuteWithParams Test" id="ot1" inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:inputText label="City" id="it1" value="#{bindings.searchCityName1.inputValue}" autoSubmit="true"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow id="gr3">
                    <af:gridCell id="gc3">
                        <af:region value="#{bindings.showlocatiobycitybtf1.regionModel}" id="r1"/>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

This concludes the first implementation and we can run the application

The sample application can be downloaded form ADFEMG Sample Project. It contains a second page (Start2) which uses the other view object (LocationsWithParamsView) inside the region. It’s build like the first version. The difference is that the default activity nor is the executeWithParams from the VOs operations instead the self implemented method from the VO. You spare writing the method and exposing the method in the client interface this way.
Be aware that the sample uses the HR DB schema and you have to change the connection information to point to your DB.

JDeveloper 11g R1: Advanced Multi Column Table Sort

A question on the JDeveloper and ADF Community Space found my attention. A user asked how to sort an af:table after more then one column.
Well, there is the official way, which Frank Nimphius’s bloged about in ‘Declarative multi-column sort for ADF bound tables’.
However this declarative approach needs the user to select the columns and their sort order. In most cases the sort after a second column is driven by the use case specification. A sample would be that the departments tables should normally be sorted after the column selected by the user, but then the data should always be sorted by the department name inside the first sort.
The image below shows the Departments table sorted first after the LocationId and inside the LocationId sorted by the DepartmentName.

Departments sorted after LocationID and DepartmentName

Departments sorted after LocationID and DepartmentName

Now lets see how to implement this. There are some possible solutions:

  1. add a sort criterion in a managed bean
  2. add a sort Criterion in the ViewObject
  3. a combination of 1) and 2)

All solutions have their advantages and disadvantages. Let’s start with the managed bean approach. This is pretty simple as we only need to add sortListener to the af:table which is pointing a bean method. In the sample below we are using the departments table where we wire up the secondary sort to the DepartmentName column.

<af:table value="#{bindings.DepartmentsView.collectionModel}" var="row" rows="#{bindings.DepartmentsView.rangeSize}"
...
    sortListener="#{DepartmentSearchBean.sortTableListener}">
...

And the sortTableListener in the bean

    public void sortTableListener(SortEvent sortEvent) {
        //log the selected column (just for information)
        List<SortCriterion> criteria = sortEvent.getSortCriteria();
        for (SortCriterion sc : criteria) {
            logger.info("Sort after: " + sc.getProperty());
        }
        // Create new SortCriterion for DepartmentName in ascending order
        SortCriterion scNew = new SortCriterion("DepartmentName", true);
        // Add it to the list
        criteria.add(scNew);
        // and apply it back to the table
        Object object = sortEvent.getSource();
        RichTable table = (RichTable) object;
        table.setSortCriteria(criteria);
        logger.info("----------------------END----------------------");
    }

That’s all we need to do to get the output from the first image. You’ll notice, that both columns are showing the sort icon. Only the one for the DepartmentName can’t change to descending order as we wired things up to always sort in ascending order. From the users point of view this can be disturbing as it’s not obvious why this happens.

For the second solution we use the model layer instead of the view layer. Here we implement the ViewObjectImpl class of the EmployeesView and overwrite the setOrderByOrSortBy(…) method. This is the method the framework calls when you click on a header on the table to sort it.
Now we can hard wire the secondary sort column, as we did in the managed bean. However, let’s think about how to make this more flexible. A nice add on is that we can use the custom properties of each table attribute to define the secondary sort column. This way we can decide which columns to sort after for each of the attributes available. We can even decide to add more then one column for secondary and third sort.

The overwritten setOrderByOrSortBy method looks for the custom property named ‘SECONDARY_SORT’ and if found, creates a new SortCriterion with the column name give in the custom property. This new sort criterion is then added to the list of SortCriteria.

    @Override
    public String setOrderByOrSortBy(SortCriteria[] sortCriteria) {
        SortCriteriaImpl scNew = null;
        // iterate current sort criteria
        for (int i = 0; i < sortCriteria.length; i++) {
            logger.info("Sort: " + sortCriteria[i].getAttributeName());
            // check for SECONDARY_SORT propertie on each attribute
            int attributeIndexOf = this.getAttributeIndexOf(sortCriteria[i].getAttributeName());
            AttributeDef attributeDef = this.getAttributeDef(attributeIndexOf);
            Object object = attributeDef.getProperty("SECONDARY_SORT");
            if (object != null) {
                logger.info("Secondary sort:" + object.toString());
                scNew = new SortCriteriaImpl(object.toString(), false);
            }
        }

        if (scNew != null) {
            // Create a new array for the added criteria
            SortCriteria scNewArray[] = new SortCriteria[sortCriteria.length + 1];
            for (int j = 0; j < sortCriteria.length; j++) {
                scNewArray[j] = sortCriteria[j];
            }

            // add the new criteria
            scNewArray[sortCriteria.length] = scNew;
            //and exceute the search
            return super.setOrderByOrSortBy(scNewArray);
        }
        
        return super.setOrderByOrSortBy(sortCriteria);
    }

The image blow shows the result for the employees table which is first sorted after the ManagerId and then after the FirstName of the employee.

Sort after ManagerId and LastName

Sort after ManagerId and LastName

As you see, only the ManagerId column shows the sort icon. The secondary sort column, FirstName, doesn’t show the sort icon.

You can download the sample application, which uses the HR DB schema from the ADF-EMG ADF Samples Repository: BlogAdvancedTableSort.zip

Empty Test for String Values using Expression Language

On the OTN ADF & JDeveloper space (aka forum) I often read use cases where an action depends on the state of an af:inputText, or better the value entered in the component.
This is problematic as a String value can either be null or it can be empty. The problem is that is you want to test a String value using Expression Language (EL) in the UI, you can’t use e.g.

#{bindings.myText1.inputValue eq ''}

The solution is an existing, but mostly unknown operator of Expression Language (EL) called ’empty’. This operator checks a String value if it’s empty. Usage of the operator is a bit different from the other operators which are used after the value. The ’empty’ operator is used in front of the valeu like

#{empty bindings.myText1.inputValue}

Sample:
consider the following use case: a button should be enabled only if a inputText component is not empty. For this we can use this code

            <af:panelGroupLayout layout="scroll" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" id="pgl1">
              <af:inputText label="Label 1" id="it1" value="#{bindings.myText1.inputValue}" autoSubmit="true"/>
              <af:commandButton text="commandButton 1" id="cb1" disabled="#{empty bindings.myText1.inputValue}" partialTriggers="it1"/>
              <af:outputText value="Working (empty bindings.myText1.inputValue): #{empty bindings.myText1.inputValue} --- not working(bindings.myText1.inputValue eq '': #{bindings.myText1.inputValue eq ''}" id="ot1"
                             partialTriggers="it1"/>
            </af:panelGroupLayout> 

As you see, the af:inputText component stored it’s value in a pageDef variable (or a VO row) and submits the entered value using the autoSubmit property set to ‘true’. The af:commandButton gets enabled only if the EL
#{empty bindings.myText1.inputValue}
returns false. This is the case when you enter anything into the af:inputText. The final piece to make it work is the partial trigger on the af:button component listening to the change of the input value.
The outputText below the button is just to show that the EL
#{bindings.myText1.inputValue eq ''}
does not work correctly.

Using one ViewObject for Global Lookup Data

A user on the OTN JDeveloper & ADF forum asked how to use one ViewObject, which holds lookup data of different areads, as LOV source. This post shows how to do this.

UPDATE:
In part 2 I added a use case which shows how to use a global lookup view object to initialize a LOV component before a page loads and show the selected lookup data.

Before we begin we have to setup the data for the the sample we build to work with. For this we add two tables to the HR schema (or any other schema you have access to). One, GENERALLOOKUP, hols lookup data, which is grouped ba a type attribute.

General Lookup Data

General Lookup Data


As we see the type column is used to group the data into different areas. The task is to use one view object which queries this table as base for model driven LOVs. To show this we need another table which we use to enter values selected by a LOV for the area from the type column.
Test Table and Data

Test Table and Data

BlogGeneralLokup 018

Scripts to create the tables and insert some data into them are provided together with the sample workspace in the file ‘setup_db.sql’. After we setup the table and the data we create entity objects and view objects like we normally do. Once the eo and vo are created, we add a view criteria which we use to select a specific type of lookup data from the GENERALLOOKUP table.

ViewCriteria to Select Lookup Data

ViewCriteria to Select Lookup Data

The view criteria has one bind variable which we later use to distinguish the different groups of lookup data. Now we can setup the lov for the attributes of the LookupTestView view object. We show this for the TitleId attribute:

Setup LOV for  TitleId Attribute

Setup LOV for TitleId Attribute


Create LOV VO

Create LOV VO


BlogGeneralLokup 003 View Accessors
rename the view object to TiltelookupView andshuttle it to the selected area
Select the GeneralLookupView

Select the GeneralLookupView


Click the edit button on the top right
Edit the LOV View

Edit the LOV View


The vital part is that we switch to the ‘View Object’ node and select the view criteria ‘TypeLookupViewCriteria’ and set the bind variable to the desired type. In this case it is ‘TITLE’
BlogGeneralLokup 006 Edit View Accessor_ TitlelookupView

Select ViewCriteria and Bind Variable

Select ViewCriteria and Bind Variable

Once the list source is setup we select the id of the list source (TitlelookupView) as ‘List Attribute’ and set the TitleId as ‘View Attribute’ for the id. Finally select the ‘UI Hint’ tab and shuttle the ‘Data’ attribute to the ‘Selected’ side. The ‘Data’ attribute of the TitlelookupView will then shown in the listbox on the UI.
BlogGeneralLokup 008 Create List of Values

BlogGeneralLokup 009 Create List of Values

Setup LOV for TitleId

Setup LOV for TitleId

This concludes the implementation for the TitleId attribute. The other attributes (GenderId, PositionId and WeekdayId) are done hte same way. Only change the bind variable ‘bindType’ to the type area you are generating the LOV for.

The gallery above shows the running application. You can download the sample workspace which was build JDeveloper 11.1.1.7.0 and using the HR schema from the ADF EMG Samples side BlogStaticVOLov_V2.zip. You can open the workspace using JDev 11.1.1.6.0 without a problem. If you are asked if you like to migrate the workspace to 11.1.1.6.0 answer with Yes.

UPDATE:
In part 2 I added a use case which shows how to use a global lookup view object to initialize a LOV component before a page loads and show the selected lookup data.

Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef

In the last couple of weeks I often saw questions on OTN JDev & ADF Forum which I answered in a way that the problem can be solved by using a page variable to store the value e.g. from a dynamic list selection or the status of a check box.
Most users don’t use this feature which, or don’t know how to define a page variable. This post show in pictures how to do this.

Update:
It was mentioned to me that Andrejus Baranovskis did almost exactly a post like this back in 2011 (Page Definition Variables to Store Temporary Page Values). This slipped the search I did, so please take a look at Andrejus Baranovskis’s post too.

When you first create a page or a fragment there doesn’t exist a page definition file for the page:

Empty New Page

Empty New Page


If you switch to the ‘Bindings’ tab you don’t see any binding
Empty Binding

Empty Binding


The pagedef file will be created if you drag e.g. a view object from the data control onto the page or if you right click on hte page in design view and select ‘Go to Page Definition’
Create Page Def

Create Page Def


You then get this dialog which you accept with OK
Safety Dialog

Safety Dialog


Now the page def file is created. The only thing we see is the ‘variables’ in hte ‘Executables’ section
Freshly created Page Def

Freshly created Page Def


The variable iterator in the executable section is generated automatically. A quote from the docs (12.6 Working with Page Definition Files)about this iterator helps to understand how it can be used to store variables temporarily on a page:

Variable Iterator: Binds to an iterator that exposes all the variables in the binding container to the other bindings. While there is an iterator binding for each collection, there is only one variable iterator binding for all variables used on the page. (The variable iterator is like an iterator pointing to a collection that contains only one data object whose attributes are the binding container variables.)

Page variables are local to the binding container and exist only while the binding container object exists. When you use a data control method (or an operation) that requires a parameter that is to be collected from the page, JDeveloper automatically defines a variable for the parameter in the page definition file. Attribute bindings can reference the page variables.

A variable iterator can contain one of two types of variables: variable and variableUsage. A variable type variable is a simple value holder, while a variableUsage type variable is a value holder that is related to a view object’s named bind parameter. Defining a variable as a variableUsage type allows it to inherit the default value and UI control hints from the view object named bind variable to which it is bound.

Now let’s think about a use case where we can make use of the variable iterator. We can e.g. store the selection of a selectOneChoice component in a variable for later use or use a booleanCheckBox to make some other parts of the page visible.
OK, let us setup a demo page showing hoe to do this. First we define some variables in the variable iterator. We start with a Boolean variable for the checkBox to hide/show the ‘Demo Panel’.

1) right click on the variables in the executables section and choose ‘Insert Inside Variables’->’variable’

Create new Variable Inside Variable Iterator

Create new Variable Inside Variable Iterator


2) in the dialog set a name e.g. ‘toggleDisplayDemoPanel’ with type java.lang.Boolean
Define Name and Type

Define Name and Type


3) Now we define an attribute binding. Click the green ‘+’ sign in the Bindings section, select attributeValues
Add attribute Binding

Add attribute Binding


Select AttributeBinding

Select AttributeBinding


4) cklick OK to get the dialog where you select the iterator and the variable from it
Select the 'variable iterator'

Select the ‘variable iterator’


Select the Variable Name

Select the Variable Name


5) cklick OK to get the final binding
Final Binding

Final Binding


This will create a variable of type Boolean in the bindings of the page. This can be used to store the value of the booleanCheckBox component. For this select the booleanCheckBox in the page and in the property inspector we use the expression builder to set the value like we do with attributes from the data control.
In this case ‘#{bindings.toggleDisplayDemoPanel1.inputValue}’ and set the autoSubmit property to true for the checkBox
Expression Builder

Expression Builder


to show/hide the ‘Demo Panel’ we setthe same EL to the visible property of the ‘Demo Panel’ and add a partial trigger pointing to the selectBooleanCheckbox
Demo Panel

Demo Panel


Now, when we run the application
Start of Application

Start of Application


select the check box to show the panel
Showing the 'Demo Panel'

Showing the ‘Demo Panel’


One other thing we can do is to define a variable of type Integer and store the selected value of a selectOneChoice showing a dynamic lov in it. This will be shown below the selectOneChoice component. Via a button ‘Reset Selection’ the selection of the selectOneChoice can be cleared by setting the variable back to null.
Select a location from the selectOneChoice
Select a Value

Select a Value


Click the ‘Reset Selection’ button
Click 'Reset selection'

Click ‘Reset selection’


This is don without any java code by using a setPropertyListener defined on the button

        <af:commandButton text="Reset Selection" id="cb1">
          <af:setPropertyListener from="#{null}" to="#{bindings.varDepId1.inputValue}" type="action"/>
        </af:commandButton>

You can download the sample, which is using the HR DB schema, from ADF EMG Samples Project BlogVariableIterator.zip. The sample was built with JDeveloper 11.1.1.6.0

JDeveloper 11.1.1.6.0: Self Closing Popup

Based on Frank’s (Frank Nimphius) article How to auto-dismiss af:popup dialogs and the sample 105, a user on OTN JDev & ADF forum tried to get this running on JDeveloper 11.1.1.6.0. Franks sample was built using JDeveloper 11.1.2.3.0 and can’t be run without changes in JDeveloper 11.1.1.6.0.
However, it’s easily possible to build a sample using 11.1.1.6.0 showing how to use the af:poll to close a popup using an af:noteWindow or an af:dialog inside the popup. The technique is described in Frank’s article, so I spare repeating it here.
Let’s look at the running application

Running Sample

Running Sample

Clicking on the link “Show Popup NW” or the button “Popup NW” shows the location of the department in a note window

Location in Note Window

Location in Note Window

Clicking on the link “Show Popup DW” or the button “Popup DW” shows the location of the department in a dialog window (modal)

Location in Dialog

Location in Dialog

You can download the sample, which is using the HR DB schema, from ADF EMG Samples Project

UPDATE 2013-03-25:
as it turned out there is a problem with the af:poll component and the timeout setting. This timeout setting should cancel the poll component after the given period of time (which should work in 11.1.2.3). This was one key feature in Frank’s original article. Using 11.1.1.6.0 it turned out, that hte af:poll remains active even after the timeout period should have canceled any further events to the poll listener.
I implemented a fix for this behavior. The popup uses a fetch listener which sets the interval (5000 ms in the sample) and the poll listener cancel the poll by setting the interval to -1 after hiding the popup.
The code is written in a generic way to allow handling of different popups. Please download the new sample from ADF EMG Samples Project