JDeveloper: JavaScript Digital Clock in a Fragment

This post is about a combination of best practices combined together. The best practices are the UI-Manager pattern (The UIManager Pattern | The Groundside Blog by Duncan Mills), using JavaScript in a fragment (Gotcha when using JavaScript in ADF Regions | Oracle OTN Harvest: Oracle ADF and MAF Blog) and running a JavaScript function when the fragment is loaded the first time (Lazy Initalizing Beans).

Use Case

We create a digital clock in a fragment. This sounds simple, but there are a couple of things to think about.

  1. How to set up a digital clock in JavaScript
  2. A fragment is not a document, so we can’t use the af:document to load JavaScript in a fragment
  3. Calling a JavaScript method on the inital load of the fragment from a bean
  4. A fragment is a task flow built from .jsff files which are ui:components in 12.2.1.4
  5. A fragment can be used multiple times on one or more pages. It should be a boundary of work. There should be no link to the outside but parameters passed to the task flow.

Implementation

We combine the best practices mentioned in the intro to implement the use case.

Digital Clock

The first part is simple. To implement a digital clock we can use JavaScript. The JavaScript handles the update of the time in the fragment. We can search the WWW for a peace of JavaScript. I end up with the two function below:

 function startTime()  {

      var today = new Date();

      var h = today.getHours();

      var m = today.getMinutes();

      var s = today.getSeconds();

      m = checkTime(m);

      s = checkTime(s);

      document.getElementById('txt').innerHTML = h + ":" + m + ":" + s;

      t = setTimeout('startTime()', 500);

 }

 function checkTime(i) {

     if (i < 10) {

        i = "0" + i;

     }

     return i;

 }

Looking at the code should ring some bells. This code should no used as is in any ADF application as it has some problems (if it runs in the first place).

Making the JavaScript usable in a fragment

The function startTime() uses document.getElementById(…) to find a specific element on the page by it’s ID. This might work on a single page but is problematic in ADF where naming containers (like table or regions) will change the ID of the elements inside the container. This is needed as a region (a task flow build from fragments) can be used multiple times on one page. If an element in the region would have the same ID every time you would not know which to address. This is the reason the naming container changes the ID of the elements inside.

Now that we know that the ID will change we change the JavaScript to an ADF digestible format

  function startTime(id) {

      //console.log('ID=' + id);

      var today = new Date();

      var h = today.getHours();

      var m = today.getMinutes();

      var s = today.getSeconds();

      m = checkTime(m);

      s = checkTime(s);

      var text = AdfPage.PAGE.findComponentByAbsoluteId(id);

      var time = h + ":" + m + ":" + s;

      text.setValue(time);

      t = setTimeout(startTime, 500, id);

  }

  function checkTime(i) {

      if (i < 10) {

          i = "0" + i;

      }

      return i;

  }

First, note that the function startTime(…) has got an ID as a parameter. This ID is the clientID of the component that we use to show the time. The method to locate the right component uses this clientID too. Finally, the setTimeout(…) function had to be changed to pass the clientID as a parameter.

With these changes, an ADF RichOutputText component with the given clientID gets updated every 500ms.

Loading JavaScript in a fragment

The next part is how to load the JavaScript as part of a fragment. This is described in https://blogs.oracle.com/jdevotnharvest/gotcha-when-using-javascript-in-adf-regions by Frank Nimphius. The short blog tells us to use an af:panelFormLayout and inside this component to add the af:resource to load the JavaScript.

The af:panelFormLayout doesn’t need any other component, its just used as trigger to load the script. Instead of adding the JavaScript directly into the resource tag, we can load a JavaScript file.

Calling a JavaScript function on the initial load of the fragment

The final task is to call the startTime(…) method on the initial load of the fragment providing the right clientID for the af:outputText that shows the time.

I mentioned before, that a fragment doesn’t have an af:document tag (and can’t have one!). This is a problem as there is no ‘onLoad’ JavaScript event available for fragments. Depending on the use case you can use the default action of the task flow and inject the needed JavaScript call from there. However, in our use case, this isn’t possible as we need the af:outputText to be available. This is only the case when the fragment renders and not when the default method is called before the fragment is rendered.

Here Lazy Initalizing Beans comes to help. We need the clientID of the af:outputText to pass it to the startTime(…) method. We use The UIManager Pattern | The Groundside Blog by Duncan Mills best practice to get the clientID.

We bind the af:outputText component to a bean in backingBeanScope (REMEMBER: don’t bind components to a bean in scope higher than request!) to get the clientID inside the naming container on the page. Make sure that the clientComponent property is set to true as you don’T get the clientID otherwise!

The next thing to do is to use the lazy init technique to inject a JavaScript call to startTimer(…) passing the clientID of the bound component. As this should only be done once, we need to set a flag that we can use to know if the initialization has already be done. The bean in backingBeanScope doesn’T live long enough for this. We need a bean in pageFlowScope. In the task flow, we define a bean with the name DigitalClockContext (DigitalClockContextBean.java) in pageFlowScope. This bean holds the status information for the region defined by the task flow. In our case a flag that shows us that the timer in the region has been started by injecting a JavaScript call to startTimer(…) method.

The DigitalClockContextBean.java looks like

It is just holding the flag initTime. The second part of the lazy init is done, when the value property of the af:outputText is read from the DigitalClockBean in backingBeanScope. We set the value property to an EL that points to the getTime() method in the DigitalClockBean

In the bean, the method checks the status of the flag from the DigitalClockContext and if not already initialized injects a JavaScript method call to startTimer(…) with the clientID from the af:outputText that is bound to the same DigitalClockBean.

In the getTime() method we first check if the bounded component is already set (line 35), then we get the flag from the DigitalClockContextBean, check if the flag is null. In this case, we get the clientID from the bounded component and inject the JavaScript function call to the startTimer(…) method with the clienID.

One other point is how to get the DigitalClockContextBean inside the DigitalClockBean. For this we use a managed property

This makes the DigitalClockContextBean available in the DigitalClockBean. All we need to do is to add a getter for the DigitalClockContextBean and a variable to store the value

Now we can test the task flow by adding it multiple times to a page (line 17 and 19)

This will get us this output

You can download the sample application from GitHub. It was created using JDeveloper 12.2.1.4 and don’t use a DB.

JDeveloper: af:query hide fields from ‘Add Fields’ button

A couple of weeks ago a question in the Oracle ADF Community asked for help with af:query in advanced mode. In detail, the user wanted to know how to hide some fields from the ‘Add Fields’ LOV available in the advanced mode of the af:query component.

You can hide fields declaratively in the view criteria the af:query. For this, you uncheck the queryable checkbox for the attribute. If you do this for e.g. the CommissionPct attribute

You don’t get this field when you click on the add button

But you don’t see the field also.

A field that is hidden this way cant be used in the query.

Use Case

The use case is to hide the field only from the list of fields to add. This is useful if you only want the field in the query once.

Solution

A look at the documentation reveals that there isn’t any property to do this, I didn’t find one at least.

The solution that I’m showing here is to use CSS to hide the field from the list that is built by the af:query component when you click the add fields button.

How to find what to hide?

Well, before we can hide something with CSS, we need to find out what to hide. First thing to try is to use a skin selector. However, when I looked into this, I did not find any skin selector that could be used for this. All selectors available for the af:query component are used for different things.

Before we start looking into skin and or CSS, we disable content compression to see the real selector names by setting a context parameter in the web.xml file

When we run the application after that, we can use any browsers development tools and look at the generated HTML code (here I use Chrome):

The blue marked part shows the markup for the ‘Email’ in the popup of the ‘Add fields’ button. The task is to hide this EMail from the add Fields list but to let it be available in the list of fields on the left.

Now that we found the right element, we can hide it simply by setting the CSS

display : none;

to it.

Well, it’s easy to do in the browser development tools, but how to do it in the application?One way would be to create a skin selector for the class

‘af_commandMenuItem af_commandMenuItem-menu-item’

but this would change all components using this skin.

My solution is to add the CSS directly to the JSF page using an af:resource tag like

The trick is to use CSS to address the right element. You can look up all available selectors e.g. at w3schools.com.

We are looking for a tr element that has a specific id and can use the selector shown in line 6 in the image above.

Running the application with this CSS added to the page results in

The Email field is still present once, but it can’T be added again as it is not visible in the list of fields to add.

The one disadvantage using this technique is that we have to specify the element by its name. This is not dynamic but a fixed value. Ich you add another query to the page you gen another id and you have to change the CSS.

In the end, it’s your decision if you use this technique, but I guess it is a valid one for this special use case.

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

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

JDev 12c: Multi Line Button

An interesting question came up in the JDeveloper & ADF ODC space. A user asked how to display a button which shows a long text in multiple lines.

The image above shows an af:button with a longer text. If you don’t have enough space in your layout to show such a long text in a button, you can shorten the Text. If this is not a acceptable, one solution is to break the long text into multiple lines.

Think about an af:panelSplitter which should show the same button

but the space i for the left pane in the splitter s limited. The result will be that the text of the button can’t be read. In other layouts the button might overflow the given space. his can crumble your whole page layout.

In this blog I’ll show you how to design a button which can handle this situation by showing the text in multiple lines. The green dotted rectangles shows the size of the layout container. This is for information only.

As you see in the image above, the text of the button breaks into multiple lines if the space is not wide enough to show it in one line. If we move the splitter to the right you see the effect

Solution

The solution is to create a style class for the button which we use for button which should be able to show their text in multiple lines. This style class is put into a skin to make it available to the ADF application.

.multiLineButton af|button::text {
    white-space: normal; 
}

The usage of the style class is simple as we see in the sample code for the af:panelSplitter

<af:panelSplitter id="ps1" splitterPosition="100" orientation="horizontal" dimensionsFrom="parent">
	<f:facet name="first">
	        <af:button text="This Button has a very long text to show" id="b3"/>
        </f:facet>
        <f:facet name="second">
                <af:button text="This Button has a very long text to show" id="b4"
                           styleClass="multiLineButton"/>
        </f:facet>
</af:panelSplitter>

You can download the sample BlogMultilineButton (or the zipped workspace) from GihHub. The code was developed using JDeveloper 12.2.1.3 and doesn’t use a DB connection.

Update: InputNumberSpinbox without Spin inside af:query

A user asked how to get rid of the spin buttons if the InputNumberSpinbox is used in an af:query component?

Whenever you have a number attribute in a VO and use it in a view criteria which you then use to show an af:query using this view criteria, the af:query uses an af:inputNumberSpinBox in the query panel to allow the user to enter values. The problem is, that you can’t control how the components rendered inside an af:query is rendered. There are no properties you can change which are available if you use the same component directly.

af:query with af:inputNumberSpinBox and spin buttons

Using the af:inputNumberSpinBox in the af:query has the same advantage as I mentioned in the original post.

And the same disadvantage too. In most cases, you don’t want or need the up/down buttons to select a number. Well, this can be done by adding the same style class we added to the af:numberInputSpinBox to the af:query component. The result can be seen in the below image

And we get the same behavior inside the af:query component too.

The reason this works is, that the af:query uses an af:inputNumberSpinBox in the query panel. The skin selector we used in the skin file works for the af:numberInputSpinBox too.

As you see, if you select the af:query in the skin file and hover over the spin button, it tells you which selector is used. This is the same selector we use in our skin file

I added another sample which I extended from the original one build with JDeveloper 12.2.1.1.0. This sample uses the HR DB schema and can be downloaded from GitHub BlogInputSpinBoxWithoutSpinV2

InputNumberSpinbox without Spin

ADF offers a wide range of components which allow user to input data. There is a build in intelligence which chooses the ‚right‘ component for the given data type when you create the UI from a data control. This allows e.g. to create a form to input data which e.g. covers the basic formatting and error handling of the data types for the given fields.

From my point of view, one wrong decision is to use the af:inputNumberSpinbox for Integer and BigInteger data types. Setting a bigger number using the spin boxes isn’t working for most people, at least not for me.

The spin buttons are of no real use in most cases. In some versions of JDev the buttons are skinned too small so that it’s hard to use them at all. There are some cases, when the range of numbers is minimal, where using the spin buttons is OK.

What I like about the component is the build in error handling if I try to input anything but a number

without doing anything to the component. This is the code used for the above image

<af:inputNumberSpinbox label="Spinnumber" id="ins2"
    value="#{bindings.myNumber21.inputValue}"/>

As you see there in nothing but the component, still we get the right error message.

You can get the same result by using a normal af:inputText with an included af:numberConverter, but you need to know how to do this:

<af:inputText label="Number in af:inputText" id="it1"
    <af:convertNumber type="number" id="nc1" pattern="0"/>
</af:inputText>

This doesn’t look identical but close enough. One difference to note is that the af:inputText starts the input on the left whereas the af:inputNumberSpinbox aligns the numbers to the right. You can change this too with setting more properties on the component.

For this I like to use the af:inputNumberSpinbox without the spin buttons.

To make the af:inputNumberSpinbox usable I get rid of the spin buttons:

The component works like hte one with the spin buttons but look like a normal inputText

This can be done by changing the skin. If you like it can be done globally or you define a custom skin class and add this class where you don’t want to see the spin butons:

.nospin af|inputNumberSpinbox::incrementor-icon-style {
    display: none;
}

.nospin af|inputNumberSpinbox::decrementor-icon-style {
    display: none;
}

The ‘.nospin‘ is the name of the custom style class you can use on the af:inputNumberSpinbox to turn the spin buttons off.

Here is the part of the page

<af:panelGroupLayout id="pgl2" layout="vertical" inlineStyle="padding-left:20px;">
    <af:inputNumberSpinbox label="Number" id="ins1" value="#{bindings.myNumber1.inputValue}" styleClass="nospin"/>
    <af:spacer width="10" height="30" id="s1"/>
    <af:inputNumberSpinbox label="Spinnumber" id="ins2"
        value="#{bindings.myNumber21.inputValue}"/>
    <af:spacer width="10" height="30" id="s2"/>
    <af:inputText label="Number in af:inputText" id="it1">
       <af:convertNumber type="number" id="nc1" pattern="0"/>
    </af:inputText>
    <af:spacer width="10" height="30" id="s3"/>
    <af:button text="Submit" id="b1"/>
</af:panelGroupLayout>

You can download the sample from GitHub BlogInputSpinBoxWithoutSpin. The sample was built with JDeveloper 12.2.1.1.0 but should work with other versions too. There is no DB used or needed to run the sample.

Jdev 12c: Implementing SQL IN Clause in an ADF ViewObject Query or ViewCriteria (Part 2)

In part one, showed how to implement a SQL IN clause in ADF. Now I show how to use this technique in a ViewCriteria or directly in a query of a view object.

We have to solve a couple of problems before we can really use the technique from part one in a ViewCriteria. As you know, when using a ViewCriteria, you select an operator which in turn is translated into SQL code. So, we have to find a way to create a new operator which will then be used to create the needed SQL code.

The technique to do this comes from an older post. Please look at Extending ViewCriteria to use SQL CONTAINS where I showed the basics on how to do this. The older post was designed for JDeveloper 11.1.2.1.0. Using the current JDeveloper version 12.2.1.3 give some ways for improvement of the earlier code.

The first improvement is that JDeveloper 12.2.1.3 allows us to introduce custom operators to view criteria. In the older sample, I had to use the description field of the ViewCriteria to pass information which SQL to generate. Now we can define an operator named ‘IN’ and use it in the ViewCriteria like any other default operator.

The next problem is how to generate the SQL shown in part one when the new custom operator ‘IN’ should be used. One of the big advantages of ADF is reusability. We use a base class which extends from ViewObjectImpl and use this new base class in the project.

The base class is named BaseViewObjectForSqlInClause were we implement the needed method

public String getCriteriaItemClause(ViewCriteriaItem aVCI)

which gets called for each part or item of a ViewCriteria. See the code of hte base class below.

Base Class

public class BaseViewObjectForSqlInClause extends ViewObjectImpl {
    private static ADFLogger _logger = ADFLogger.createADFLogger(BaseViewObjectForSqlInClause.class);

    // comma-separated list of custom operators. Each custom operator muast have a ',' at the end as delimeter!
    private static final String CUSTOM_OPERATORS = "IN,";

    public BaseViewObjectForSqlInClause(String string, ViewDefImpl viewDefImpl) {
        super(string, viewDefImpl);
    }

    public BaseViewObjectForSqlInClause() {
        super();
    }

    /**
     * Check if a given criteria item tries to use an 'IN' operator using a bind parameter (comma seperated list of strings).
     * Create special SQL clause for 'IN' operator
     * @param aVCI Criteria item
     * @return where clause part for the criteria item
     */
    @Override
    public String getCriteriaItemClause(ViewCriteriaItem aVCI) {
        // we only handle the SQL 'IN' operator
        String sqloperator = aVCI.getOperator();
        // add comma to operator as delimiter
        boolean customOp = CUSTOM_OPERATORS.indexOf(sqloperator.concat(",")) >= 0;
        customOp |= sqloperator.indexOf("NVL") >= 0;
        if (customOp) {
            ArrayList<ViewCriteriaItemValue> lArrayList = aVCI.getValues();
            if (lArrayList != null && !lArrayList.isEmpty()) {
                // check if the criteria item has bind parameters (only the first if of interest here as the IN clause onlyallows one parameter)
                ViewCriteriaItemValue itemValue = (ViewCriteriaItemValue) lArrayList.get(0);
                if (itemValue.getIsBindVar()) {
                    // get variable and check if null values should be ignored for bind parameters
                    Variable lBindVariable = itemValue.getBindVariable();
                    Object obj = ensureVariableManager().getVariableValue(lBindVariable.getName());
                    boolean b = aVCI.isGenerateIsNullClauseForBindVariables();
                    if (b && obj == null) {
                        // if null values for bind variables should be ignored, use the default getCriteriaItemClause
                        return super.getCriteriaItemClause(aVCI);
                    }

                    try {
                        // we only handle strings data types for bind variables
                        String val = (String) obj;
                    } catch (Exception e) {
                        // the bind variabel has the wrong type! Only Strings are allowed
                        _logger.warning("Bind variabel for SQL " + sqloperator +
                                        " clause is not of type String! -> No custom SQL clause created! (Class: " +
                                        obj.getClass() + ", Content: " + obj + ", Variable: " +
                                        lBindVariable.getName() + ", View: " + this.getName() + ")");
                        String s = ":" + lBindVariable.getName() + " = :" + lBindVariable.getName();
                        return s;
                    }

                    // only handle queries send to the db
                    if (aVCI.getViewCriteria()
                            .getRootViewCriteria()
                            .isCriteriaForQuery()) {
                        String sql_clause = null;
                        switch (sqloperator) {
                        case "IN":
                            sql_clause = createINClause(aVCI, lBindVariable);
                            break;
                        default:
                            _logger.severe("Unknown custom operator '" + sqloperator + "' found! -> do nothing!");
                            break;
                        }

                        return sql_clause;
                    } else {
                        // bind variable not set or
                        // for in memory we don't need to anything so just return '1=1'
                        return "1=1";
                    }
                }
            }
        }

        return super.getCriteriaItemClause(aVCI);
    }

    private String createINClause(ViewCriteriaItem aVCI, Variable lBindVariable) {
        // start build the sql 'IN' where clause (COLUMN is the name of the column, bindParam the name of the bind variable):
        // COLUMN IN (SELECT regexp_substr(:bindParam,'[^,]+',1,level) FROM dual CONNECT BY regexp_substr(:bindParam,'[^,]+',1,level) IS NOT NULL
        // get flagg to create an sql where clause which ignores the case of the bind parameter
        boolean upper = aVCI.isUpperColumns();
        String sql_in_clause = null;
        StringBuilder sql = new StringBuilder();
        if (upper) {
            sql.append("UPPER(");
        }
        sql.append(aVCI.getColumnNameForQuery());
        if (upper) {
            sql.append(")");
        }
        sql.append(" ").append(aVCI.getOperator());
        sql.append(" (select regexp_substr(");
        if (upper) {
            sql.append("UPPER(");
        }
        sql.append(":");
        sql.append(lBindVariable.getName());
        if (upper) {
            sql.append(")");
        }
        sql.append(",'[^,]+', 1, level) from dual connect by regexp_substr(");
        if (upper) {
            sql.append("UPPER(");
        }
        sql.append(":").append(lBindVariable.getName());
        if (upper) {
            sql.append(")");
        }
        sql.append(", '[^,]+', 1, level) is not null)");
        sql_in_clause = sql.toString();

        _logger.finest("generated SQL-IN clause: " + sql_in_clause);

        return sql_in_clause;
    }
}

Using Base Class in Project

To use the base class in all new created ViewObjects of the project, we change the models project properties

Now, whenever you create a new ViewObject, the new base class is used and the SQL IN operator can be used in the VOs view criteria.

You can change any existing ViewObject to use the BaseViewObjectForSqlInClause by changing the extends clause in the class definition by hand.

Creating a ViewCriteria Using the Custom IN Operator

All pieces are in place and using the IN operator is pretty easy. We start by creating a new ViewObject named EmployeesOfDepartmentsViewCriteria

Now we have a ViewObject based on an EntityObject for the Employees. We need to make one change. The DepartmentId is an Integer type attribute, the comma-separated list is of type String (containing numbers). This doesn’t match. We add another attribute to the ViewObject of type String which we calculate from the DepartmentId Integer attribute. We change the SQL query for this by selecting the ‘Query’ node first unselecting the checkbox ‘Calculate Department Query at Runtime (recommended)’, second select the checkbox ‘Write Custom SQL’ and third add the line ‘to_char(Employees.DEPARTMENT_ID) DEPARTMENT_ID_STR,’ to the query.

Once this new ViewObject has been created, we add a ViewCriteria to it

If you like, you can turn off the checkbox ‘Ignore Case’ as it is not needed. The numbers are always lower case.

Running the ApplicationModule in the Tester

At this stage, we can test run the application module in the Application Module Tester (see JDeveloper & ADF: Use the Application Module Tester (BC4J Tester) to Test all your BusinessLogic).

Click the binocular button to select the ViewCriteria we created and click ‘Find’

Which will open a dialog asking for the value of the bind variable

Clicking ‘OK’ will show the result as

Running the ViewCriteria on a Page

Finally, we can add the ViewCriteria to a page as af:query and test it there. I’ll spare the exact howto here and just show hte running application.

Or with different parameters and spaces

You can download the sample application from GitHub BlogSqlInClause.

The Sample was built using JDeveloper 12.2.1.3 (but it should work in all 12c versions) and uses the HR DB schema.

JDeveloper 12c: using Expression Language in pageDef to switch ControllerClass

An interesting question came up late 2018 in the JDeveloper & ADF forum. A user asked how to use Expression Language (EL) in a pageDef file to switch the ControllerClass at runtime depending on some condition of a page.

The ControllerClass can be used to add custom code into the lifecycle of a page or fragment (see the full details at 27.4 Customizing the ADF Page Lifecycle). A tip

Tip:
You can specify the value of the page definition’s ControllerClass attribute as a fully qualified class name or you can enter an EL expression that resolves to a class directly in the ControllerClass field.
When using an EL expression for the value of the ControllerClass attribute, the Structure window may show a warning indicating that e “#{YourExpression}” is not a valid class. You can safely ignore this warning.

given in the documentation mentioned that you can use EL to specify the ControllerClass. The missing information is exactly how to do it.

Use Case

For a JSF page, a ControllerClass should be defined at runtime. The selected ControllerClass should depend on a condition.

Solution

There are a couple of blogs available which use a custom ControllerClass in a pageDef, but they use the direct specification of the custom class in the pageDef. Only one sample (https://github.com/oracle/adf-samples/releases/download/v1.0.0/OnPageLoad.zip) from Duncan Mills uses EL to set the ControllerClass. However, this sample was built for JDev 10.3.1! None of the samples I found use EL to switch the ControllerClass at runtime.

In summary, it’s time for a fresh sample using JDev 12.2.1.3

Building the UI

We start by creating a new Fusion Web Application from the gallery. The steps to follow can be looked up at Why and how to write reproducible test cases, so I skip them here.

Once the basic Fusion Web Application is built we open the adfc-config.xml (the unbounded ADF task flow) and add a page onto it.

We name the page index and create it using a Quick Layout. I normally use

but you can use whatever layout like. We add a Text to the header section and an af:inputText and an af:button to the content section. The page markup will look 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="index.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="Using EL to switch ControllerClass" id="ot1"
                                       inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:panelGroupLayout id="pgl2" layout="horizontal">
                            <af:inputText label="Use Listener" id="it1"/>
                            <af:button text="Update" id="b1"/>
                        </af:panelGroupLayout>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Next, we open the properties of the af:button and edit the actionListener property. Here we create a new bean, name it ‘IndexBean’, create a new method ‘updatePage’. The bean we create in sessionScope. Once the bean has been created, we delete the actionListener from the af:button we just created. The ‘updatePage’ method in the IndexBean we delete too. We don’t need the listener and I only used it to create the bean in the right scope for other things.

I created the bean in session scope as I will use it to store the data from the inputText we added to the page. This is just a convenience, we could have used a pageDef variable for this.

We add a String property ‘usePPListener’ to the IndexBean add the needed getter and setter methods.

public class IndexBean {
    String usePPListener = "1";

    public IndexBean() {
    }

    public void setUsePPListener(String usePPListener) {
        this.usePPListener = usePPListener;
    }

    public String getUsePPListener() {
        return usePPListener;
    }
}

We set the default value of the ‘usePPListener’ to “1”. We set the ‘usePPListener’ to the value property of the af:inputText field of the page. And set the autoSubmit property of the af:inputText to true. The page markup for the content:

<af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
    <!-- Content -->
    <af:panelGroupLayout id="pgl2" layout="horizontal">
        <af:inputText label="Use Listener" id="it1" value="#{IndexBean.usePPListener}"
                      autoSubmit="true"/>
        <af:button text="Update" id="b1"/>
    </af:panelGroupLayout>
</af:gridCell>

Running the application at this stage shows

Creating the PagePhaseListener Class

Now we can go on and create the custom PagePhaseListener classes which we then use to switch using an EL. To create such a custom class, the documentation tells us that all we have to do is to create a class which implements the ‘PagePhaseListener’ interface.

This will create the first PagePhaseListener named ‘MyPagePhaseListenerA’. The resulting class looks like

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

import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;

public class MyPagePhaseListenerA implements PagePhaseListener {
    public MyPagePhaseListenerA() {
        super();
        System.out.println("MyPagePhaseListenerA created");
    }

    @Override
    public void afterPhase(PagePhaseEvent pagePhaseEvent) {
        // TODO Implement this method
        System.out.println("MyPagePhaseListenerA afterPhase called");
    }

    @Override
    public void beforePhase(PagePhaseEvent pagePhaseEvent) {
        // TODO Implement this method
        System.out.println("MyPagePhaseListenerA beforePhase called");
    }
}

We do it again but name the class ‘MyPagePhaseListenerB’. As you see I added some System.out.println(“…”) statements so that we can see which PagePhaseListener is used later.

Using EL in pageDef.xml

Finally, we start with the interesting part, the EL to use in the pageDef ControllerClass property to switch the listener. The use case demands that we switch the PagePhaseListener depending on a condition. The condition we use is pretty simple:

  • Use MyPagePhaseListenerA if the inputText value stored in usePPListener is equal to “1”
  • Use MyPagePhaseListenerB if the inputText value stored in usePPListener is equal to “2”

We have two options to make the decision,

  1. directly in EL in the pageDef
  2. use a method in a bean where we check the condition

The essential part to know is the type of result both options need to return to work. The ControllerClass expects an object of type PagePhaseListener, or a class which implements this interface.

Solution 1

The classes we created before are implementing this interface, so they should work. To implement option 1 we need to need to instantiate an object of one of the classes and return it on the EL like in the pageDef.xml:

ControllerClass=”#{IndexBean.usePPListener eq ‘1’ ? MyPagePhaseListenerA : MyPagePhaseListenerB}”

And we need to register the bean named ‘MyPagePhaseListenerA’ and ‘MyPagePhaseListenerA’ in the task flow. This will instantiate the object when the task flows starts.

Running this application this way we get

Entering ‘2’ into the af:inputText and clicking ‘Update’ we get

Heureka! The PagePhaseListener have switched as expected.

Solution 2

We implement a method in a bean which returns the right PagePhaseListener class. The EL in the pageDef.xml looks like

ControllerClass="#{IndexBean.pagePhaseListener2Use}"

The method getPagePhaseListener2Use() we implement in the IndexBean as

public PagePhaseListener getPagePhaseListener2Use() {
    if (getUsePPListener() != null && getUsePPListener().equals("2")) {
        return new MyPagePhaseListenerB();
    } else {
        return new MyPagePhaseListenerA();
    }
}

The method returns the PagePhaseListener interface instead of the real class object. This is necessary as a method can only return one object type.

Running the application in this configuration results in exactly the same output we saw before.

Using EL in PageDef for Fragments used in Regions

If you plan to use this technique for fragments which run in regions you would need to return a RegionController instead of a PagePHaseListener. However, as it turned out, you can’t use EL at all in a pageDef of a fragment!

The problem is, that the framework simply doesn’t evaluate the EL you specify for the ControllerClass of a fragment, but uses the EL as the class name. This results in a ‘ClassNotFoundException’ as there is no class named ‘#{your_EL}’.

I’m not sure if this is a bug or a feature. I could not think of a valid use case to use EL for a fragment pageDef.

If you do have one, share it in the comments to this post, please.

If you really think you need to switch the behavior for a RegionController at runtime, you can create one Class which implements the different in the overwritten methods and decide which part to execute inside the method.

Sample

You can download the sample application, which was built using JDev 12.2.1.3 and no DB connection, from GitHub BlogELPageDef