JDeveloper 11gR1 Bug in Tuning Node of ViewObject

Today I came across a bug in the tuning node of ViewObjects in JDeveloper 11gR1 (meaning all 11.1.1.x versions). For a prove of concept I played with the tuning options available for ViewObjectes in JDeveloper. The following image shows the the default tuning node of a ViewObject (it doesn’t matter if it’s based on an EntiyObject or not):

Default Tuning Node

Default Tuning Node

As I tested some options I eventually switched to the ‘Only up to row number’ radio button which enables the input field for the number of rows:

Tuning for '...up to row number'

Tuning for ‘…up to row number’

Nothing special there. Now if you delete the number (default is 10)

Delete Number from Field

Delete Number from Field

and ‘tab’ out of the input field, or click on any other field, you get an error dialog telling you that ‘(null) is not a valid fetch size value’.

Error Dialog

Error Dialog

OK, this is correct, but if you hit the OK button or the ‘x’ to close the dialog to put the number back in the dialog stays open, you can’t close it. You don’t get a chance to put the number back into the field. A first I had to kill JDeveloper through the task manager (I did that a couple of times ;)) until I found the following workaround:
            hit the Esc key
you may need to do this multiple times, but the dialog closes eventually and the last number is back in the input field.

This bug is fixed in the current JDeveloper 11gR2 (11.1.2.3.0) version!

Advertisements

JDeveloper: Execute Bean Method on Hitting Enter in af:inputText Component

This blog article describes a common use case. A user enters some value into an field on the page and his enter. This should trigger a method on the server (e.g. a bean method).

Use case
Hitting ENTER on a af:intputText component should trigger an action (e.g. executing a bean method).

Implementation
The sample we implement in this blog shows a page with a splitter component. On the left side we see an input text component which is used to enter a value. On the right side we see the result of the search as a table of countries from the HR schema. The search condition used is to show all countries which names starting with the value entered in the input text component.

Sample Application

Sample Application

As you see there is no button ot other command component to execute the search. The search is triggered by hitting enter in the input text field on the left side of the splitter.

Filtered Output after hitting Enter

Filtered Output after hitting Enter

To implement this we need to use JavaScript af:clientListener to handle the keyboard input. The javascript method then queues a server action using a af:serverlistener.

<af:inputText label="Country" id="it1" value="#{bindings.SelCountryName1.inputValue}" autoSubmit="true">
    <af:clientListener method="handleEnterEvent" type="keyPress"/>
    <af:serverListener type="EnterEvent" method="#{SendEnterBean.handleEnterEvent}"/>
</af:inputText>

The af:clientListener listens for keyPress event and in the JavaScript method checks weather the key pressed was the enter key or not. If it was the enter key it queues an event for a server side method in a bean. The method name is given in the af:serverListener type=”EnterEvent” method=”#{SendEnterBean.handleEnterEvent}”. The method listen for the event type send from the client AdfCustomEvent.queue(comp, “EnterEvent”, {fvalue:comp.getSubmittedValue()}, false);. This type is defined in the serverListener as type property.

<af:resource type="javascript">
    function handleEnterEvent(evt) {
      var _keyCode = evt.getKeyCode();
      //check for Enter Key
      if (_keyCode == AdfKeyStroke.ENTER_KEY ){    
          var comp = evt.getSource();
          AdfCustomEvent.queue(comp, "EnterEvent", {fvalue:comp.getSubmittedValue()}, false);
          evt.cancel();
      }
   }
</af:resource>

The server side method now calles the “executeWithParams” method on the courntries iterator which filters the countries table for countries starting with the value given by the input field.

    public void handleEnterEvent(ClientEvent ce) {
        _logger.info("Got event " + ce.getType());
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
        // get an Action or MethodAction
        OperationBinding method = bindings.getOperationBinding("ExecuteWithParams");
        if (method == null) {
        _logger.info("Method ExecuteWithParams not found in current bindings");
        return;
        }

        // get the parameter from the event
        String message = (String) ce.getParameters().get("fvalue");
        //This can be used too if one doesn't like to send the parameter to the method
        // get an ADF attributevalue from the ADF page definitions
        //AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("SelCountryName1");
        //String v = (String)attr.getInputValue();

        //Set the parameter
        Map params;
        Map map = method.getParamsMap();
        map.put("bindName", message);

        method.execute();
        // check for errors
        if (!method.getErrors().isEmpty()){
            Exception ex =(Exception) method.getErrors().get(0);
            _logger.warning("Error: " + ex.getLocalizedMessage());
        }

        // PPR refresh a jsf component
        AdfFacesContext.getCurrentInstance().addPartialTarget(countriesTable);
    }

The missing part is how the countries table get filtered. For this in the model layer we created a ViewObject “CountriesView” for the COUNTRIES table of the HR schema. Then we define a ViewCriteria “CountriesByNameVC”:

ViewCriteria CountriesByNameVC

ViewCriteria CountriesByNameVC

In the data model of the application module we use the view object instance CountriesView1 and select the ViewCriteria as query.

Edit View Instance CountriesView1 to use ViewCriretia CountriesByNameVC

Edit View Instance CountriesView1 to use ViewCriretia CountriesByNameVC

Now in the ViewController project we only need to store the value entered by the user in the af:inputText field and add add a method binding for the “executeWithParams” method which we use in the bean method to filter the table.

Final pageDef

Final pageDef

and the source for the pageDef file:

<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="11.1.1.60.13" id="BSE2BPageDef" Package="de.hahn.blog.sendenter2bean.view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables">
      <variable Name="SelCountryName" Type="java.lang.String"/>
    </variableIterator>
    <iterator Binds="CountriesView1" RangeSize="25" DataControl="BSE2BAppModuleDataControl" id="CountriesView1Iterator"/>
  </executables>
  <bindings>
    <tree IterBinding="CountriesView1Iterator" id="CountriesView1">
      <nodeDefinition DefName="de.hahn.blog.sendenter2bean.model.dataaccess.CountriesView" Name="CountriesView10">
        <AttrNames>
          <Item Value="CountryId"/>
          <Item Value="CountryName"/>
          <Item Value="RegionId"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
    <attributeValues IterBinding="variables" id="SelCountryName1">
      <AttrNames>
        <Item Value="SelCountryName"/>
      </AttrNames>
    </attributeValues>
    <action IterBinding="CountriesView1Iterator" id="ExecuteWithParams" RequiresUpdateModel="true" Action="executeWithParams">
      <NamedData NDName="bindName" NDValue="#{bindings.SelCountryName1.inputValue}" NDType="java.lang.String"/>
    </action>
  </bindings>
</pageDefinition>

You can download the sample, which is build using JDeveloper 11.1.1.5.0 and uses the HR schema, from ADF Sample Source Code Repository

JDeveloper: Using Static ViewObjects for Lookup Data used by e.g. LOV

This blog shows how to set up a static ViewObject in a model project and use this static data as lookup data in an other ViewObject as LOV. Static lookup data can and should be put into a shared application module, as the data seldom change during the live of the application using the static data. This we’ll leave for another blog. Here we create a model project and create a simple static ViewObject which we then use in another as choice list.
The sample was build using JDeveloper 11.1.1.6.0 and uses the HR schema. To get the workspace use the link provided at the end of this blog.

Use case:
We need some static data as lookup for an other attribute which resides in a view object. In this sample we use the COUNTIRES table from the HR schema which has an foreign key RegionId to the REGIONS table from the HR schema. Instead of using the REGIONS data directly, we setup a static view Object which holds the data to be shown for the foreign key in the UI.
To show the difference, we also setup the lookup data using the REGIONS table data.

Implementation:

We start with setting up the workspace. We use the normal ‘Fusion Web Application’ template. The template creates two projects, one model and one view controller project. once the names for the projects are entered, we start with the model project. We select the model project and select ‘Create Business Components from Table’. After copying the HR Connection to the project, we select the Countries and the Regions as entities

Entity Objects: Countries and Regions

Entity Objects: Countries and Regions

Next we select the two ViewObjects based on the EntityObjects build in the previous step

Updatable ViewObjects

Updatable ViewObjects

as we don’t need read only ViewObjects for this blog we skip this step and finally set a name for the application module

Application Module

Application Module

Next we create a new ViewObject which we define as ‘Rows populated at design time (Static List)’ which we use as lookup table.

Create Static VO

Create Static VO

Define as Static List

Define as Static List

Define the attributes and their data types. In this case we use an id (Number) and a String type as region name.

Attribute Id

Attribute Id

Attribute RegionName

Attribute RegionName

Next after the attributes for the static view are defined we add the values. Here you can enter as many rows as you like. There also can be more then two attributes. The sample only needs hte two attributes.

Empty Static List

Empty Static List

Filled Static List

Filled Static List

For the sample we define a second view object based on the COUNTRIES table of the HR schema. We use the two identical VO to show how to setup the LOV for the RegionId once using the entity based VO RegionView and once to setup the LOV using the static VO created before.

Create second VO to demonstrate setup of LOV using Static VO

Create second VO to demonstrate setup of LOV using Static VO

VO is based on Countries EO

VO is based on Countries EO

Use all Attributes

Use all Attributes

After the attributes are defiend we setup the LOV for the RegionId. For this we select the RegionId attribute and open the ‘List of Vaule:’ for the RegionId

Define the List Data Source for the LOV

Define the List Data Source for the LOV

Clicking the green plus sign we get the dialog where we define the choice list for the RegionId

Select the Static View as List Source

Select the Static View as List Source

Select Static VO as List Source

Select Static VO as List Source

Select the List Id

Select the List Id

Now as we want to see the region name instead of the Id we switch to the ‘UI Hints’ tab and select the regionName attribute from the list source

Select the RegionName

Select the RegionName

Shuffle it to the 'selected' side

Shuffle it to the ‘selected’ side

The same action are done for the CountriesView, only here we select the RegionsView as list source. Finally we add the new views to the data model of the application module. All other views we delete as we do’t need them.

Final Application Module

Final Application Module

Now we can test run the application module:

Test CountriesView

Test CountriesView

Test Countries4StaticRegionView1

Test Countries4StaticRegionView1

As expected we once see the region name from the regions table and once we get the static data we added to our static VO.

The final step for this blog is to setup a page in hte UI. For this we create one page with an af:panelSplitter. on hte left side we drag the CountriesView1 from the data control and drop it as ‘ADF Table…’. On hte right we drag the Countries4StaticRegionView1 and drop it as ‘ADF Table…’ too. We set the table to select single and allow sorting. Finally we set the table to ‘Click to Edit’ mode so that only on row can be edited at once.

Running Application: CountriesView1

Running Application: CountriesView1

Running Application: Countries4StaticRegionView1

Running Application: Countries4StaticRegionView1

Download:
You can download the workspace of the sample from here: ADF EMG Samples: BlogStaticVOLov.zip. The sample uses the HR schema and was build using JDeveloper 11.1.1.6.0 but should run in 11.1.1.5.0 and 11.1.2.x too.

JDeveloper & ADF: Carefully Select the Data Control Scope of Bounded Task Flows

A customer asked me to look into an error which showed up in one of their applications. Without going into the details of real use case I like to bring this issue to your attention as it might happen to you too.

Use Case
I set up a mock use case to show the problem as this: A page (.jspx or .jsf) includes one (or more) other task flows as a region. The base page retrieves some data using a method call exposed in the data control, which should be displayed in the region for information purpose.

Implementation
If the task flows share their data controls this is not a big deal to implement this. You can download the sample using the link provided at the end of this blog. The sample uses the HR schema and was build using JDev 11.1.1.5.0 and was additional tested with JDev 11.1.1.1.6.0 and JDev 11.1.2.2.0, which show the same behavior.

On the base page I set up an inputText which I use to pass a parameter to the method (callMeTest) which retrieves some info from the application module. The callMeTest method simply returns the passed parameter together with a ‘nice’ message:

    public String callMeTest(String name) {
        _logger.info("Called with: "+name);
        return "Call me Test "+name;
    }

The return value is then displayed on the base page at the bottom under the button and should be displayed in the region, under the table, too. The sample just uses one region. When no value is present ‘****’ is used as parameter. The image below shows the application after starting it.

Start Screen

Start Screen

After entering a value into the inputText field below the table and hitting the ‘callMeTest’ button underneath it, you see that the return value is displayed below the button on the base page and under the table inside the region. If the button is hit with another value, both values are updated.

After entering a value and hitting the button

After entering a value and hitting the button

Nothing unusual so far. Now, for some unknown reason, after a change to another part the application, that functionality did not work any longer. The value in the base page still updates after the call to the method, but the value in the region stopped updating.

Screen after next deployment

Screen after next deployment

The reason for this behavior is a simple one. A look into the task flow properties of the region, revealed that the checkmark for the ‘Share data controls with calling task flow’ was not set, meaning that the task flow get its own data control frame. The result is that the method outcome from the base page is not visible in the region.

TaskFlow properties (1)

TaskFlow properties (1)

The question was how did this happen, when the change which results in the new deployment had nothing to do with this part of the application?

Analysis
It turned out, that when you create a new bounded task flow this property value is set to ‘default’. This is shown in the ‘Overview’ of the task flow as bluish shade in the check box. No checkmark is set and a look into the source reveals that the whole section

    <data-control-scope>
      <....../>
    </data-control-scope>

is just missing from the XML file.

TaskFlow after creation

TaskFlow after creation

Once you click on this property in the property editor you can just switch between ‘isolated’ or ‘shared’.

TaskFlow properties (2)

TaskFlow properties (2)

The only possible way to get the bluish shade back is to delete the section in the source view of the task flow.

TaskFlow properties in 'Source Mode'

TaskFlow properties in ‘Source Mode’

So this checkbox has three states ‘bluish’, ‘isolated’, and ‘shared’. Well, there is a forth state which you get if you open the property editor in source mode and select ‘default’. The following table shown all states together with their meaning:

State  Meaning
Bluish  Shared
Check marked  Shared
Unchecked not bluish  Isolated
Data-control-scope empty  Isolated

While the application works according to the meaning of the flag, I find it not intuitive how JDev handles this setting. The programmer who made the change to the application clicked this property, but meant to do so on a different task flow. So he unchecked the ‘Share data controls with calling task flow’ property as he remembered that it was not set before.

Recommendation
My advise is to set the flag whenever you create a task flow so you know which share mode the task flow uses. This avoids errors like the one described in this blog.

UPDATE
In the meantime Chris Muir raised the issue with the UX specialists (Oracle intern) and Bug 14390576 has been raised for this issue.
Thank you Chris!

The sample uses the HR schema and was build with JDeveloper 11.1.1.5.0, tested with 11.1.1.6.0 and 11.1.2.2.0 . You can download the sample work space form ADF Samples: Source Code

JDeveloper ADF af:query Component: How to Toggle Display Modes

ADF af:query component comes with many functions and layout possibilities. This post describes how to toggle between the ‘basic’ and the ‘advanced’ display mode from java code.
Fist lets see the difference between ‘basic’ and ‘advanced’ mode.

af:query in 'basic' mode

af:query in ‘basic’ mode

and the same component switched to ‘advanced’ mode:

af:query in 'advanced' mode

af:query in ‘advanced’ mode

You see the difference is that the user can change the conjunction used for each attribute. There is a button available on the af:query component which you can click to toggle the mode. However there are use cases where you need to do this from inside a java bean. When you check the properties available for hte component you’ll notice, that you can change the position and the visibility of the button

Properties to Change the Toggle Button

Properties to Change the Toggle Button

So the user is able to change the two modes by clicking the button. The problem is that you can’t start in advanced mode. Only when you create your own ViewCriteria in the ViewObject you can switch to the ‘UI Hints’ tab and select how you like to start the query region:

Select Display Mode for ViewCriteria

Select Display Mode for ViewCriteria

So you either create your own ViewCriteria and select to start in ‘advanced’ or you need to do it from a java bean as described below. The sample application is build using JDeveloper 11.1.1.5.0 and uses the HR schema. The link to download the sample can be found at the end of the post.

Use Case: lets assume we want to show an af:query panel in advanced mode to only a specific user named ‘King’. All other users should only see the ‘basic’ af:query panel. For this we use an inputText on the left side of a af:panelSplitter which is used to input the user name. When the value of this inputText changes we check if the name is ‘King’ and switch the af:query to ‘advanced’ mode, otherwise we set it to ‘basic’.

Implementation:
To implement the use case we have to do two things. First we need an inputText to enter the user name. In reality this name should be get from a security context. Here it’s enough to store the value entered by the user to a binding attribute, set the inputText component to autoSubmit and implement a valuechangeListener where we put the logic to switch the mode of the af:query to ‘advanced’ or to ‘basic’.
We start with setting up a variable in the executable section of the pageDef:

Create a Variable in Executable Section of pageDef

Create a Variable in Executable Section of pageDef

Name the Variable

Name the Variable

Create an Attribute Binding for the Variable

Create an Attribute Binding for the Variable

Create Attribute Binding 2

Create Attribute Binding 2

Create Attribute Binding 3

Create Attribute Binding 3

Create Attribute Binding 4

Create Attribute Binding 4

Create Attribute Binding 5

Create Attribute Binding 5

Now after the attribute binding is in place we can set up the inputText for the name and set the value property to the created binding attribute, set the autoSubmit property and the valueChangeListener to a bean method

Set value Property

Set value Property

Set autoSubmit and valueChangeListener Properties

Set autoSubmit and valueChangeListener Properties

The valueChangeListener we bind to a bean in request scope (BTAFQBean) and implement the method ‘nameChangeListener’ as

    public void nameChangeListener(ValueChangeEvent valueChangeEvent) {
        // get the queryPanel which is bound to the bean property
        RichQuery qp = getQueryPanel();
        Object newValue = valueChangeEvent.getNewValue();
        String nameNew = (String)newValue;
        Object oldValue = valueChangeEvent.getOldValue();
        String nameOld = (String)oldValue;
        // if old value != King and new value == king  set advanced
        if ("King".equalsIgnoreCase(nameNew) &&
            !("King".equalsIgnoreCase(nameOld))) {
            // switch queryPanel to advanced mode
            qp.getValue().changeMode(QueryDescriptor.QueryMode.ADVANCED);
            //ppr the query panel
            AdfFacesContext.getCurrentInstance().addPartialTarget(qp);
        } 
        // if New value != King and old value == kng switch to basic
        else if ("King".equalsIgnoreCase(nameOld) &&
                   !("King".equalsIgnoreCase(nameNew))) {
            // switch queryPanel to basic mode
            qp.getValue().changeMode(QueryDescriptor.QueryMode.BASIC);
            //ppr the query panel
            AdfFacesContext.getCurrentInstance().addPartialTarget(qp);
        }
    }

The af:queryPanel is bound to a bean property in the BTAFQBean for convenience. This makes it easy to get the component and sent it a PPR from java code. The essential part of the code is located in line 12 and 20. The method to switch the mode is hidden in the query descriptor which you get as the value of the af:query component. gp.getValue() get us the QueryDescriptor which exposes the changeMode() method. The parameter is a constant, either QueryDescriptor.QueryMode.BASIC or QueryDescriptor.QueryMode.ADVANCED.

When you run the application and enter e.g. ‘peter’ into the inputText field and leave the field (or hit return) the query panel looks like

Query Panel in BASIC Mode

Query Panel in BASIC Mode

Now type ‘king’ into the name field and leave the field. As a result you see

Query Panel in ADVANCED Mode

Query Panel in ADVANCED Mode

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on hte HR db schema, from here: http://java.net/projects/smuenchadf/sources/samples/content/BlogToggleAFQuery_V1.zip