JDeveloper 12.2.1.4: responsive af:panelFormLayout

One of the new notable features in JDeveloper 12.2.1.4 is an extension to the af:panelFormLayout component that allows a responsive behavior of the component.

af:panelFormLayout before 12.2.1.4

The image above shows the typical af:panelFormLayout. Actually we see two af:panelFormLayout:

  1. At the top, we see an af:panelFormLayout where we can alter the layout with the af:selectOneChoice. This we use to investigate the new setting
  2. The one at the bottom that just shows the old af:panleFormLayout without any change. This is just there to better see the difference

In the image above both look the same. There is no responsiveness in either panelFormLayout. If the browser window gets smaller than the needed size for the two columns you get

We see that the label now takes two rows, but the inputText components in the second column are clipped.

Changes for 12.2.1.4

JDeveloper 12.2.1.4 added a new property ‘layout’, that changes this behavior to make the component more responsive to changes in the width of the window size.

The new property can have one of three possible values:

  1. Weighted: this is the default. It will size the panelFormLayout to the size to the content and then break the labels if the space gets too small.
  2. Fixed: will size to the width of the screen and truncate the labels and fields
  3. Responsive: will auto arrange the fields in multiple columns and manage labelAlignment based on the space available for the panel

Weighted layout

As this is the default layout (and the only one before 12.2.1.4) we skip this here.

Fixed layout

The image above shows the difference between ‘fixed’ layout (top part) and ‘weighted’ layout (bottom part). Reducing the width of the screen will result in

Still not what we call responsive.

Responsive layout

OK, let’s look at the ‘responsive’ property. If the container width if big enough, it almost looks like the ‘fixed’ layout (see image below).

When the width is reduced the labels are arranged above the input field, which quite an improvement over the weighted layout.

When the width is reduced further, the multi-column layout is reduced to a one-column layout

By default, ADF defined three points or width where the layout changes. These points are defined in the skin and can be changed by altering the skin

af|panelFormLayout {
  -tr-panel-size-sm: 768;
  -tr-panel-size-md: 1024;
  -tr-panel-size-lg: 1281; /* anything greater than this is xl */
}

If you need different points where the layout should change, you can easily change the skin and define different style classes for different scenarios.

The sample was built using JDeveloper 12.2.1.4.0 and can be found at GitHub under BlogResponsivePanelFormLayout.

JDeveloper 12.2.1.4: Exposing Custom Methods from ADFbc ViewObjects to REST API

The new JDeveloper version 12.2.1.4 brought some changes to the ADF business components REST features. One of the features I wondered about is how to expose custom methods built in a view object to the REST API. The release documentation at (https://docs.oracle.com/en/middleware/developer-tools/adf/12.2.1.4/develop/whats-new-this-guide-release-12c-12.2.1.4.0.html#GUID-F0E4CCC6-5419-4FFD-B2F3-E29B40A487DB) says

What does this mean?

The REST part in the documentation still shows some pictures showing the ‘Custom Method’ tab for a REST resource

However. Searching the documentation for ‘custom method’ doesn’t show any result.

Defining a public custom method in the employee’s view and adding it to the client interface (only then methods are visible from the outside)

Looking at the same dialog in JDeveloper 12.2.1.4 does not show the ‘custom method’ tab 😦

So, is the documentation correct?

NO, a hint from @JDeveloper in a tweet gives a hint on this.

Methods are exposed by default!

But how to find them?

Well, let’s try the ‘describe’ operation on the VO using the URL ‘http://127.0.0.1:7101/BlogAdfRestMethod-RESTWebService-context-root/rest/v0/Employees/describe’. This will return plenty of information about the REST API. There we find the information about the Method ‘getVersionInfo’ which I wrote as a custom method in the EmployeesViewObjectImpl class.

To see the method in the REST API, the method must be part of the client interface. This is what the tweet means by ‘updating the metadata’, I guess.

Now, to call the method we use a POST request with the content type set to ‘application/vnd.oracle.adf.action+json’ as noted in the description of the method. The body of the POST request is set to

{
    "name" : "getVersionInfo"
}

Sending the request will give us

I added another method to the EmployeesViewObjImpl method

Named ‘sayHello’. This method I did not expose to the client interface. As we see in the description, this method is not exposed in the REST API. After exposing the method to the client interface too, we get this method in the REST API too.

Now, the description lists

So, the new method can be called via REST too

You can download the sample application from GitHub BlogAdfRestMethod. The sample was built using JDeveloper 12.2.1.4 and uses the HR DB schema.

JDeveloper 12.2.1.4: af:chooseDate

The new JDeveloper 12.2.1.4 has added an enhancement an a new feature to the af:chooseDate component. We are looking at both in this article.

Enhancement

The enhancement is that you can now set a default date which is shown as the selected date when the field you show the af:chooseDate for is empty. In the older JDeveloper versions, the selected date was always the current date. The image below shows the situation of how the af:chooseDate component looks like:

The af:inputText ‘Datum’ is not set and because of this, the af:chooseDate shows the current date (when I took the picture) as the selected date. Problem is, that there are use cases where you don’T want the current date to be the selected date, e.g. your use case might want to show the next Friday after the current date as the selected date. This was not easily possible.

Now, the enhancement to the af:hcooseDate component allows us to set a default date via the property ‘defaultValue’. When set to a literal value, this will be parsed as “yyyy-MM-dd hh:mm:ss” or “yyyy-MM-dd”.

I added an af:inputdate ‘Set DefaultDate’ to allow selection of a date which then will be used as the default date in the af:chooseDate for the ‘Datum’ field. The current date is marked with gray and the date set in the ‘Set Default Date’ is shown as the selected date.

As you see, the ‘Datum’ field is still empty. If you select a date like ‘12/20/2019’ you get

Sample Code

<af:inputDate label="Set Default Date:"
     Nhn ng  value="#{bindings.defaultDate1.inputValue}" id="id2"
     autoSubmit="true"/>
<af:spacer id="s1" height="10px"/>
<af:panelGroupLayout id="pgl2" layout="horizontal">
<af:inputDate label="Datum" id="id1" chooseId="cd1"/>
<af:chooseDate id="cd1"
    defaultValue="#{bindings.defaultDate1.inputValue}"
    partialTriggers="id2"/>

New Feature: “Multiple Date Selection”

The af:chooseDate component has another feature added. Now you can select multiple dates at once. This feature is very handy as you can now use one af:chooseDate to get a start date and an end date or date ranges. The image below shows the component with multiple dates selected.

The interesting thing is, that there is no implementation for this hardcoded in the component. Meaning is that we, the developers, need to implement this feature ourselves. For this ADF provides two new client events ‘load’ and ‘dateSelection’ that are triggered by the af:chooseDate component. We use these client events to implement the multi-select feature. The load event can be used to pass an array of dates to the component which are then shown as selected. This event is triggered when the component loads, as the name implies. The dateSelection event is triggered each time a user selects a date. It passes information about the keyboard state so that we can handle range selection.

To make it easier, the ADF Rich Client Demo has a sample implementation of the needed JavaScript code which I used as the base of my implementation:

/**
 * Shows a popup from an "action" type event.
 * @param {AdfActionEvent} actionEvent the event being handled
 */
var dates = [];
var minDate;
var maxDate;

function dateSelectionEventHandler(event) {
    var eventSource = event.getSource();
    var selectedDate = event.getSelectedDate();
    var modifier = event.getModifiers();

    if (modifier.indexOf(AdfRichChooseDate.MULTI_SELECTION) !=  - 1) {
        dates.push(selectedDate);
    }
    else if (modifier.indexOf(AdfRichChooseDate.RANGE_SELECTION) !=  - 1) {
        if (!minDate || (minDate.getTime() > selectedDate.getTime())) {
            minDate = selectedDate;
        }
        if (!maxDate || (maxDate.getTime() < selectedDate.getTime())) {
            maxDate = selectedDate;
        }
        var timeDiff = Math.abs(maxDate.getTime() - minDate.getTime());
        var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24)) + 1;
        dates = []
        for (var i = 0;i < diffDays;i++) {
            var selDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate() + i, 0, 0, 0, 0);
            if (!eventSource.isDisabled(selDate))
                dates.push(selDate)
        }
    }
    else if (modifier.indexOf(AdfRichChooseDate.SINGLE_SELECTION) !=  - 1) {
        minDate = null;
        maxDate = null;
        dates = []
        dates.push(selectedDate)
    }
    eventSource.setSelectedDates(dates);
}

function chooseDateLoadEventHandler(event) {
    var eventSource = event.getSource();
    eventSource.setSelectedDates(dates);
}

function processSelectedDates(event) {
    component = event.getSource();
    AdfCustomEvent.queue(component, "processSelectedDates", 
    {
        payload : dates
    },
    true);
    event.cancel();
}

There are three JavaScript funtions:

  1. dateSelectionEventHandler(event) handles the selection of one or more dates
  2. chooseDateLoadEventHandler(event) handles the inital load of hte component and allows us to set dates as selected
  3. processSelectedDates(event) is the funtion which passes the selected dates to a server event

The functions are added to the af:chooseDate compponent as client listerner

<af:chooseDate id="cd2" clientComponent="true">
    <af:clientListener type="dateSelection" 
        method="dateSelectionEventHandler"/>
    <af:clientListener type="load" method="chooseDateLoadEventHandler"/>
</af:chooseDate>

To pass the selected dates to a server event a button is used like

<af:button text="Selected Dates" id="b2">
    <af:clientListener method="processSelectedDates" type="action"/>
    <af:serverListener type="processSelectedDates"
        method="#{viewScope.ChooseDateBean.procressSelectedDates}"/>
 </af:button>

The image below shows the selected dates once the button ‘Selected Dates’ is clicked.

This JavaScript function simply passes the array of selected dates as payload from the client to the server. There the dates are just stored in a bean variable. This variable is used to show the dates in an af:outputText.

The same works for a range selection as shown below

And after the button ‘Selected Dates’ is clicked we get

The sample can be downloaded from GitHub BlogChooseDate or directly as a zip. The sample is written using JDeveloper 12.2.1.4 and doesn’t use any DB connection.

JDeveloper 12.2.1.4: af:panelTabbed

The new JDeveloper 12.2.1.4 provides some new features and enhancements. In the next couple of posts we are going to investigate some of them.

We start with the af:panelTabbed component. There is an enhancement fro this component we all waited for a long time. Vertical stacked tabs with icons and text. This sounds like a small thing, but in earlier versions of ADF vertical stacked tabs could only show icons.

We are calling tabs vertical stacked, if the tab is on the left or right side of the panel. Horizontal stacked tabs are the ones at the top or the button of the panel.

Vertical Stacked Tab with Icons and Text

As said before, in earlier versions of ADF you needed to do a workaround to get tabs on the left or right showing an icon, text or both. You needed to create an icon from the icon, Text or icon with text and add it to the af:showDetailItem. The drawback was that you couldn’t just edit the text (e.g. correcting a spelling mistake) or had to generate different such icons for different languages along with some logic to exchange the icon according to the selected language.

The new af:panelTabbed has a new property ‘verticalTabMode‘ with two modes:

  • iconOnly this mode behaves like the old versions and only shows an icon defined for the af:showDetailItem
  • iconAndText this mode shows an icon and the text defined in the text property of the af:showDetailItem
af:panelTabbed with verticalTabMode=”iconOnly’
af:panelTabbed with verticalTabMode=”iconAndText’

The images above show the af:panelTabbed with the new verticalTabMode. The sample application for this can be downloaded from GitHub BlogPanelTabbed. It does not use a DB connection and was built using JDeveloper 12.2.1.4.