Sunday, May 22, 2016

Responsive design in Oracle ADF


Nowadays the need to create a web application based on a responsive design is becoming very common. ADF in its latest version (as of today), i.e. 12.2.1 provides the capability to address this. There is a feature in ADF called Match Media Behavior which helps the developer to set the layout and placement of UI components in a page based on the screen size. This enables the appropriate rendering of the page regardless of whether it is being rendered in a desktop, tablet or a mobile device. In this way, customers can see the possibilities of getting away from building a mobile app, which requires additional infrastructure, separate code base etc.

In this blog, I am providing a simple example of using the matchMediaBehavior tag. My page has a bar chart and a table. When the page is rendered in a desktop device, these components should render horizontally, and when it is rendered in a tablet device, these components should be displayed vertically, so that the table will be rendered below the bar chart.

Here is the code of the page:

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                xmlns:dvt="http://xmlns.oracle.com/dss/adf/faces" xmlns:f="http://java.sun.com/jsf/core">
  <af:panelGroupLayout id="pgl1" layout="horizontal">
    <af:matchMediaBehavior matchedPropertyValue="vertical" propertyName="layout" 
  mediaQuery="screen and (max-width: 900px)"/>
    <dvt:barChart orientation="vertical" id="barChart1" var="row" value="#{bindings.EmpDetailsVO1.collectionModel}">
      <dvt:chartLegend id="cl1"/>
      <f:facet name="dataStamp">
        <dvt:chartDataItem id="di1" series="#{bindings.EmpDetailsVO1.hints.Salary.label}" value="#{row.Salary}"
                           group="#{row.DepartmentName}"/>
      </f:facet>
    </dvt:barChart>
    <af:table value="#{bindings.EmpDetailsVO11.collectionModel}" var="row" rows="#{bindings.EmpDetailsVO11.rangeSize}"
              emptyText="#{bindings.EmpDetailsVO11.viewable ? 'No data to display.' : 'Access Denied.'}"
              rowBandingInterval="0" selectedRowKeys="#{bindings.EmpDetailsVO11.collectionModel.selectedRow}"
              selectionListener="#{bindings.EmpDetailsVO11.collectionModel.makeCurrent}" rowSelection="single"
              fetchSize="#{bindings.EmpDetailsVO11.rangeSize}" id="t1">
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.FirstName.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.FirstName.label}" id="c1">
        <af:inputText value="#{row.bindings.FirstName.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.FirstName.label}"
                      required="#{bindings.EmpDetailsVO11.hints.FirstName.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.FirstName.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.FirstName.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.FirstName.tooltip}" id="it1">
          <f:validator binding="#{row.bindings.FirstName.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.LastName.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.LastName.label}" id="c2">
        <af:inputText value="#{row.bindings.LastName.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.LastName.label}"
                      required="#{bindings.EmpDetailsVO11.hints.LastName.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.LastName.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.LastName.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.LastName.tooltip}" id="it2">
          <f:validator binding="#{row.bindings.LastName.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.Salary.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.Salary.label}" id="c3">
        <af:inputText value="#{row.bindings.Salary.inputValue}" label="#{bindings.EmpDetailsVO11.hints.Salary.label}"
                      required="#{bindings.EmpDetailsVO11.hints.Salary.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.Salary.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.Salary.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.Salary.tooltip}" id="it3">
          <f:validator binding="#{row.bindings.Salary.validator}"/>
          <af:convertNumber groupingUsed="false" pattern="#{bindings.EmpDetailsVO11.hints.Salary.format}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.DepartmentName.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.DepartmentName.label}" id="c5">
        <af:inputText value="#{row.bindings.DepartmentName.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.DepartmentName.label}"
                      required="#{bindings.EmpDetailsVO11.hints.DepartmentName.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.DepartmentName.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.DepartmentName.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.DepartmentName.tooltip}" id="it5">
          <f:validator binding="#{row.bindings.DepartmentName.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.JobTitle.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.JobTitle.label}" id="c6">
        <af:inputText value="#{row.bindings.JobTitle.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.JobTitle.label}"
                      required="#{bindings.EmpDetailsVO11.hints.JobTitle.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.JobTitle.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.JobTitle.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.JobTitle.tooltip}" id="it6">
          <f:validator binding="#{row.bindings.JobTitle.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.City.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.City.label}" id="c7">
        <af:inputText value="#{row.bindings.City.inputValue}" label="#{bindings.EmpDetailsVO11.hints.City.label}"
                      required="#{bindings.EmpDetailsVO11.hints.City.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.City.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.City.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.City.tooltip}" id="it7">
          <f:validator binding="#{row.bindings.City.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.StateProvince.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.StateProvince.label}" id="c8">
        <af:inputText value="#{row.bindings.StateProvince.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.StateProvince.label}"
                      required="#{bindings.EmpDetailsVO11.hints.StateProvince.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.StateProvince.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.StateProvince.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.StateProvince.tooltip}" id="it8">
          <f:validator binding="#{row.bindings.StateProvince.validator}"/>
        </af:inputText>
      </af:column>
      <af:column sortProperty="#{bindings.EmpDetailsVO11.hints.RegionName.name}" sortable="true"
                 headerText="#{bindings.EmpDetailsVO11.hints.RegionName.label}" id="c10">
        <af:inputText value="#{row.bindings.RegionName.inputValue}"
                      label="#{bindings.EmpDetailsVO11.hints.RegionName.label}"
                      required="#{bindings.EmpDetailsVO11.hints.RegionName.mandatory}"
                      columns="#{bindings.EmpDetailsVO11.hints.RegionName.displayWidth}"
                      maximumLength="#{bindings.EmpDetailsVO11.hints.RegionName.precision}"
                      shortDesc="#{bindings.EmpDetailsVO11.hints.RegionName.tooltip}" id="it10">
          <f:validator binding="#{row.bindings.RegionName.validator}"/>
        </af:inputText>
      </af:column>
    </af:table>
  </af:panelGroupLayout>
</ui:composition>


Tip: To test the behavior of page’s rendition in a tablet device, you can decrease size of the browser window.

When the page is rendered in desktop device


When the page is rendered in tablet device

Sunday, January 31, 2016

OAF Error : java.lang.IllegalArgumentException: Unknown signal: ALRM


I have seen questions in OTN forum where people seeking clarification on the below error that they see in the log while running OAF pages from Jdeveloper.

java.lang.IllegalArgumentException: Unknown signal: ALRM


This error message can safely be ignored, as per Oracle. And the justification is,
Signal handler was added in order to provide additional debug facilities such that when the ALRM signal is sent to the JVM process, it will dump out AM statistics/information to the console.  The signal handler is only used for diagnostic purposes so this error will not impact the JDeveloper runtime.


You may as well check the MOS note, java.lang.IllegalArgumentException: Unknown signal: ALRM when using JDeveloper With OA Extension (Doc ID 431140.1).

Saturday, January 16, 2016

Change the Color of Prompt and Value of Text field in OAF

In this blog, I try to handle a scenario where we need to set the color of Prompt and Value of a text field in an OAF page.
We will not be able to set the CSS for the text in the prompt of a messageTextInput field. I am sharing a workaround here for that.
We create a rowLayout and two cellFormats with in it. The first cellFormat will have a staticStyledText which holds the Prompt text and the second cellFormat will have a messageTextInput field which holds the Value.

Page XML Code
<oa:header id="TestPromptColRN">  
   <ui:contents>  
   <oa:rowLayout id="RL">  
   <ui:contents>  
   <oa:cellFormat id="Cell1">  
      <ui:contents>  
      <oa:staticStyledText id="SST1" text="PO Number" prompt="PO Number"/>  
      </ui:contents>  
   </oa:cellFormat>  
   <oa:cellFormat id="Cell11">  
      <ui:contents>  
      <oa:messageTextInput id="MTI1"/>  
      </ui:contents>  
   </oa:cellFormat>  
   </ui:contents>  
   </oa:rowLayout>  
   </ui:contents>  
</oa:header>  


Java code to be placed in the processRequest of the Page's Controller.
CSSStyle customCss = new CSSStyle();  
customCss.setProperty("color", "#FF0000");  
OAStaticStyledTextBean sstBean =   
    (OAStaticStyledTextBean)webBean.findChildRecursive("SST1");  
if (sstBean != null)  
    sstBean.setInlineStyle(customCss);  
OAMessageTextInputBean mtiBean =   
    (OAMessageTextInputBean)webBean.findChildRecursive("MTI1");  
if (mtiBean != null)  
    mtiBean.setInlineStyle(customCss); 


The output will be like.



Tuesday, January 5, 2016

Disabling autocomplete in an OAF page

I have come across a question in OTN forum where a user asked to secure the credit card number entry in the Message text field of OAF.
Basically the user was looking for an option to turn off the Auto-Complete of the input field.
He cannot make the field as a Password field, because after data entry the number has to be legible to the user to confirm back with the customer.

Here is the solution given :-

If you have only one field in your page where you want to turn off the Auto complete, you can use the below.

OAMessageTextInputBean textInBean = (OAMessageTextInputBean)webBean.findIndexedChildRecursive("item1");
textInBean.setNoAutoComplete(true);


If you want to turn off the Auto complete for whole page, you can use the below code.

pageContext.putJavaScriptFunction("disableAutoComplete","function disableAutoComplete() { document.DefaultFormName.autocomplete='off';}");
OABodyBean bodyBean = (OABodyBean) pageContext.getRootWebBean();
bodyBean.setOnLoad("disableAutoComplete();");

Monday, December 28, 2015

SQL - DECODE truncates the date value

I would like to share a tip related to SQL in this blog. I had a PL/SQL package that updates a record in the database table with a DATE value based on some condition.
The Update statement was like :-

UPDATE XX_TAB
SET    xx_updated_date = decode(cur.ship_num,null,null,sysdate)
WHERE  record_id  = cur.record_id;

The intention was to check whether cur.ship_num is NULL or not. If it is NULL, update the date field, xx_updated_date to NULL. Otherwise update the date field with sysdate.
The update was happening properly. But the sysdate value was getting truncated. That means, it is just updating the date, not the time.

Later figured out that, since the first returnable value in the decode is a NULL, an implicit conversion of DATE value to VARCHAR2 happens(when sysdate value is returned by DECODE in this case); and the time value will be lost.
Modified the DECODE statement by adding a TO_DATE and it started working as required.

UPDATE XX_TAB
SET    xx_updated_date = decode(cur.ship_num,null,TO_DATE(null),sysdate)
WHERE  record_id  = cur.record_id;

You can find a detailed discussion related to this topic in the below thread of OTN forum.
https://community.oracle.com/thread/2255091

Tuesday, October 27, 2015

OAF – Implementing Validation View object, Validation Application Module and Entity Expert

While we implement the business logic or performing validations in entity object, we might come across situations where in we need to execute SQL statements to retrieve some value from the database.

Sample Scenario:-
While validating Sales Order in the Sales Order entity object, XXSalesOrderEO, verify whether the customer exists in the Customer master or not.

SQL:
SELECT customer_id
          , customer_name
  FROM xx_customer_master
  1. Create a validation view object, XXCustomerMasterVVO using the above SQL.
  2. Create a validation application module, XXOrderEntryVAM and add the above VVO as a VO instance. Note: The approach to create VVO and VAM is same as that of creating a regular view object and application module.
  3. Create Entity expert by simply creating a java class, XXSalesOrdEntityExpert that extends oracle.apps.fnd.framework.server.OAEntityExpert class.
  4. Register the entity expert to the Entity object as below. Note that in case of standalone EO, register to that object directly. And in case of master-detail entities, register to the top-level entity.
    1. Edit entity object in Jdeveloper and navigate to the Properties tab
    2. Add the following two properties.
      • Property 1 Name: ExpertClass
      • Property 1 Value: xx.oracle.apps.so.schema.server.XXSalesOrdEntityExpert
      • Property 2 Name: VAMDef
      • Property 2 Value: xx.oracle.apps.so.schema.server. XXOrderEntryVAM
  5. Create a method in the Entity expert class that executes the VVO 
public String customerExists(String custName) {
        String customerExists = "N";
        XXCustomerMasterVVOImpl custMasterVO = 
            (XXCustomerMasterVVOImpl)findValidationViewObject("XXCustomerMasterVVO1");
        custMasterVO.setWhereClause(null);
        custMasterVO.setWhereClauseParams(null);
        custMasterVO.setWhereClause("customer_name = '" + custName + "'");
        custMasterVO.executeQuery();

  if (custMasterVO.getRowCount() > 0)
   customerExists = "Y";
  return customerExists;
    }


      6. Consume the above Entity Expert’s method in the EntityImpl class, XXSalesOrderEOImpl  to perform the validation.

XXSalesOrdEntityExpert soExpert = (XXSalesOrdEntityExpert)getOADBTransaction().getExpert(XXSalesOrderEOImpl.getDefinitionObject())
 String   customerExists = 
                            soExpert.customerExists(custName);

Wednesday, September 16, 2015

Horizontal display of Collection data in ADF page

There was a requirement in one of my past projects to build an ADF page to enter Order quantity for different sizes of an item based on the availability of that item. Customer has asked for a layout like the below.

Size
5
6
7
Available Quantity
10
20
30
Order Quantity





Since ADF table layout is inherently vertical, page with an ADF table will be displayed as below:-


Since the horizontal display in this scenario ease the entry of Item Quantity while viewing its Availability, the users were very particular about the horizontal display, which seemed fair to me as well.
I was using Jdev 11.1.1.6. I thought of leveraging pivot table initially, but later figured that it cannot serve my purpose. Finally zeroed in on an approach to have horizontal display of data, which I would like to share with you.

  • Create a panel group layout with layout as horizontal and create two panel group layouts inside that.
  • One Panel group layout with layout as vertical will hold the Text of the Data, i.e. Item Size, Available Quantity and Order Quantity.
  • Second Panel group layout with layout as horizontal will have af:iterator component to hold the values from the collection(here it is a view object).

JSPX Code


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<af:panelGroupLayout id="pgl11" layout="horizontal"
                     inlineStyle="border:2px solid; margin:5px; padding:5px; border-color:Gray;">
  <af:panelGroupLayout id="pgIterator1" layout="vertical" >
    <af:outputText value="Item Size" id="it31"
                   inlineStyle="font-weight:bold; vertical-align:super;"></af:outputText>
    <af:spacer width="0" height="1" id="s1"/>
    <af:separator id="s5"
                  inlineStyle="border:1px solid; border-color:Gray;"/>
    <af:outputText value="Available Quantity" id="it21"
                   inlineStyle="font-weight:bold; vertical-align:super;"></af:outputText>
    <af:spacer width="0" height="3" id="s2"/>
    <af:separator id="s6" inlineStyle="border:1px solid; border-color:Gray;"/>
    <af:outputText value="Order Quantity" id="it11"
                   inlineStyle="font-weight:bold; vertical-align:super;"></af:outputText>
  </af:panelGroupLayout>
  <af:panelGroupLayout id="pgl1" layout="horizontal" >
    <af:iterator id="iter1" value="#{bindings.ItemsVO1.collectionModel}"
                 var="row">
      <af:panelGroupLayout id="pgIterator" layout="vertical"
                           inlineStyle="border-left:solid 2px; margin-left:5px; padding-left:5px; border-color:Gray;">
        <af:outputText value="#{row.Itemsize}" id="it3"
                       inlineStyle="display:block; text-align:center; vertical-align:bottom;"></af:outputText>
        <af:spacer width="0" height="4" id="s3"/>
        <af:separator id="s7" inlineStyle="border:1px solid; border-color:Gray;"/>
        <af:outputText value="#{row.Atp}" id="it2"
                       inlineStyle="display:block; text-align:center; vertical-align:bottom;"></af:outputText>
        <af:spacer width="0" height="4" id="s4"/>
        <af:separator id="s8" inlineStyle="border:1px solid; border-color:Gray;"/>
        <af:inputText value="#{row.bindings.Quantity.inputValue}"
                      id="it1" contentStyle="width:22px">
          <f:validator binding="#{row.bindings.Quantity.validator}"/>
          <af:convertNumber groupingUsed="false"
                            pattern="#{bindings.ItemsVO1.hints.Quantity.format}"/>
        </af:inputText>
      </af:panelGroupLayout>
    </af:iterator>
  </af:panelGroupLayout>
</af:panelGroupLayout>


The final layout will look like this.