Thursday, 19 October 2017

Oracle ADF Insert Record in read only table










Oracle ADF “Create Insert” into read only table

The following related to Jdeveloper 11g (11.1.2.3)
Sometimes you want to present the data in the table as read-only, but still provide an option to create a new record by using build-in “create insert” functionality. Obviously we need some king of mechanism to make existing records “read-only” and new record to be “editable”.
Lacily for us  where is a property in Entity Object attribute called updatable. You can set its value to “While New” . So it will not be updatable all the time except when you are trying to create a new record.
But many times this is completely useless feature.
Consider the following table
create table xx_adf_demo (key_column number,customer_id number)

I can define customer_id  “While New” , but it doesn’t give me anything since end-user is not interested to see the customer id , but customer name. So I need to join “xx_adf_demo” with some other table/view that holds the “customer_name”.
So my View Object will not be based only on the Entity Object , but also on some other table.
The good new I can see now the customer name. The bad news  - this field is not based on the Entity Object and there is no “While New” option in View Object attributes. For now we will just set it to “Always”
Here is the solution.
I will start by defining the sequence for “key_col” attribute and setting it into default value for this column.
create sequence xx_adf_demo_s
minvalue 1
maxvalue 9999999999999999999999999999
start with 115
increment by 1
cache 20;

we will also define a new transient attribute called “isReadOnly” of Boolean type and “Updated” Always

Generate View Object Row java class and modify the “getisReadOnly” function
if (getEntityForAttribute("TagId").getEntityState() == EntityImpl.STATUS_NEW)
                    {
                      return Boolean.FALSE;
                    }
                    else
                    {
                      return Boolean.TRUE;
                    }

By using this code “isReadOnly” flag will be automatically set for “false” when row state is new.
Create another View Object to hold the LOV for customer name and assign it to customer name attribute in XxAdfDemoVO View Object.
Now create you GIU by dragging the View Object, Create Insert, Commit and Rollback Buttons to the page (Place the buttons in the Panel Collection toolbar). When you dragging the View Object to the page – choose to drop it as “Adf Table…”


If we run it now as is we will get

We see that “customer name” column is not read only. Also note the value of “isReadOnly” column
Only small modification needed to achieve what we need. Modify “Read Only” property of “CustomerName” LOV to be #{row.isReadOnly}.

Save and refresh the page. Click on the “CreateInsert”.


We are done.
http://docs.oracle.com/cd/E53569_01/tutorials/tut_adf_faces/tut_adf_faces.html

How to Identify a View Object is modified
There are cases where we need to show the modified VO rows in separate pop up window or highlight the modified/new rows .

I have a quiet interesting usecase in my development project and thought i would write about it . The usecase is tat an ADF Table in the ui page can  be modified and new rows can also be created.After modifying some of the rows the user has navigated to some other page without committing the modified data. At this point i need to stop the user and alert him saying that some of the VO rows have been modified.

This is a direct approach and can be done in two ways. Either in the managed bean or in the design time page using expressions.According to ADF new/modified rows will be in cache.

Download the Sample Application and run CatchModifiedRows.jspx page.

we will continues using the workspace which i have been using for the earlier post .
Lets start developing it. Create a jspx page CatchModifiedRows.jspx . Drag and drop EmployeesVO1 on to the page as ADF Table and keep the necessary columns you want.keep a button at the bottom of the ADF table to navigate to some other page.


Create Action Listener for the Next button and name the bean as ModifiedVOBean.java and method as chkmeth. Inside the method get the bindings for the EmployessVO1 .

        DCBindingContainer bindings2 =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
           
        JUCtrlHierBinding obj = (JUCtrlHierBinding)bindings2.findCtrlBinding("EmployeesVO1");
        ViewObject targetVO = obj.getViewObject();

we can get the current status of the view object instance using a method isDirty().

Boolean b = targetVO.getApplicationModule().getTransaction().isDirty();

This gives Boolean value of True/False . If something is modified/created the value will be True else False.



Final piece of code is

    public void chkMeth(ActionEvent actionEvent) {
   
        DCBindingContainer bindings2 =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
           
        JUCtrlHierBinding obj = (JUCtrlHierBinding)bindings2.findCtrlBinding("EmployeesVO1");
        ViewObject targetVO = obj.getViewObject();
        Boolean b = targetVO.getApplicationModule().getTransaction().isDirty();
       FacesMessage msg=null;
        Boolean ev = false;
        if(b!=ev){
        msg=new FacesMessage("Hold on ,you have unsaved data.pls commit before proceeding");
        FacesContext.getCurrentInstance().addMessage(null,msg);
        }

  }

The boolean result  can be evulated against True/False and based on it we can throw a message to the user . Now go to the page and change something in any of the column and click Next. you will get the message.Further  you can customize the implementation the way u want like information in the pop up dialog
and so on.


we can also able to get Entity level status from VO using this code

RowSetIterator rs1 =targetVO.createRowSetIterator(null);
while (rs1.hasNext()) {
  EmployeesVORowImpl r = rs1.next();
  Entity eo = r.getEntity(0);
byte currentState= eo.getEntityState();
if(currentState==EntityImpl.STATUS_NEW .......)
{
 ......
}
}

From design time we can access the row status using the following expression
Expression #{row.row.entities[0].entityState}

Getting the View object in MB and performing operations on it is not a good practice.


Insert Rows in to ADF View Object Programatically
Following are the use cases pertaining to this topic

1. Insert Rows into ADF View Object Programatically.
2. Insert Rows into a ADF View Object from a ADF Table which is a combination of multiple tables (for example : ADF Table in the jsp page contains mixture of columns from various tables and we need to take only some column data and save it in target ADF VO.

Create a View object from multiple entities (( Assuming you have a View Object SourceVO1 ).Drag and drop the View Object data control on to the page as ADF Table.Create an ADF button at the bottom of the table. Create Action Listener for the button. you must have the target View Object ready to store the values. Since you have created ADF Table on the page the binding section will have the bindings created for the View Object and to access its collection model .For more on bindings section ADF Bindings. Now we need to add the target View Object to the page bindings section.

This part of the section also describes about How to Create Tree Table binding.
Go to the Bindings section of your page. Click Plus icon

 Select tree from the Insert Item window and click ok.


Click Add button as shown . This will list all of the View Objects added to the Application Module

 Choose the desire View Object in which you are going to save the records.

Click at the plus icon and select Add Rule .


This window will bring down all of the attributes that target view object has.you can shuttle the necessary attributes to the righter side and click ok . Now the Target View Object added as Tree binding can be seen under Bindings section.


Now we will write the java code to insert rows. As we have already created an Action Listener for the button,go to the method of that java class and write this code.
//Code to get the bindings for TargetVO :
   
         DCBindingContainer bindings2 =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
         
        JUCtrlHierBinding obj = (JUCtrlHierBinding)bindings2.findCtrlBinding("ProgrammaticVO1");
        ViewObject targetVO = obj.getViewObject();


   DCBindingContainer bindings =
            (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
        DCIteratorBinding empIter =
            bindings.findIteratorBinding("SourceVO1Iterator");

//SourceVO1Iterator is the iterator under Executables section for the SourceVO1 bindings.

        RowSetIterator roleRSIters = empIter.getRowSetIterator();
        RowSetIterator rs1 = roleRSIters.getRowSet().getViewObject().createRowSetIterator(null);
        NameValuePairs nvp = null;

        while (rs1.hasNext()) {
            Key key = rs1.next().getKey();
            Row r = rs1.getRow(key);           
           
            nvp = new NameValuePairs();
            nvp.setAttribute("Empid",r.getAttribute("EmployeeId"));
            nvp.setAttribute("Nameone",r.getAttribute("FirstName"));
            nvp.setAttribute("Nametwo",r.getAttribute("LastName"));
            targetVO.createAndInitRow(nvp);
         }
       
          rs1.closeRowSetIterator();
        targetVO.getApplicationModule().getTransaction().commit();

Getting the View object in MB and performing operations on it is not a good practice.


ADF : Accessing column value from ADF table row

There are cases where we need to show only a number in the Ui that comes from a different ViewObject .. Infact the real case scenario is ViewObject that has count(*) as result and we need to print that to the user in a tab or textbox.. look at the example


The Tab displays the Total Countries ..

select countries(*) as Count from COUNTRY group by countries ; This will return a single value..we generally tend to use VO.getEstimatedRowCount to get all the rowvalues and display .. The way to achieve and retrieve this is very simple ...


Following code snippet will help to achieve that

 public int getCount() {

        Long val = 0;
        ViewObject vo = this.getCountryVO();
        vo.executeQuery();
        RowSetIterator rsIterator = vo.createRowSetIterator(null);
        rsIterator.reset();
 
       while (rsIterator.hasNext()) {
            Row row = rsIterator.next();
            val = ((BigDecimal)row.getAttribute("Count1")).longValue();
        }

        rsIterator.closeRowSetIterator();
        return val;

  }

Expose this method in the AM Client Interface .Add this method  to the page bindings as methodAction. Go to executable section in the pagedef , add invokeAction to this method . This will get invoked every time when the page is loaded . This brings the count available during pageload

Add this expr to the place where you need to print the result .
#{bindings.getCount.resul}

You can also access that method directly on clicking some button or link via ActionListener without having to InvokeAction under executables section ...
ADF : View Criteria complex code snippets

View Criteria is a declarative representation of View Object where clause. View Criteria contain a criteria row that itself contains attributes/items that the where clause constructs.


vo = this.getEmpVO();

//To get all the view criterias currently applied on a VO

String[] criterias =  vo.getViewCriteriaManager().getApplyViewCriteriaNames();


//To see all the applied view criterias on a VO

        String[] criterias =  vo.getViewCriteriaManager().getApplyViewCriteriaNames();
        if (Tcriterias != null) {
            for (String vcName : criterias) {
                System.out.println("ViewCriteria :" + vcName);
            }
        }


//To unapply a view criterias on a VO which is currently applied.

String[] criterias =  vo.getViewCriteriaManager().getApplyViewCriteriaNames();
        if (Tcriterias != null) {
            for (String vcName : criterias) {
               vo.getViewCriteriaManager().removeApplyViewCriteriaName(vcName);
            }
        }


//To see all the view criteria items inside a view criteria row

List list = vc.getRows(); //(vc is a view criteria)

Iterator iter1 = list.iterator();
while (iter1.hasNext()) {
   

                   ViewCriteriaRow row = (ViewCriteriaRow)iter1.next();

                   row.getCriteriaItem("EmpId");
                   row.getCriteriaItem("EmpName").getValue();

                   List vcitems = row.getCriteriaItems();
                  Iterator itemiter = vcitems.iterator();

                 while (itemiter.hasNext()) {
                         
                   ViewCriteriaItem vcitem = (ViewCriteriaItem)itemiter.next();
                   System.out.println("vcitemname in vcrow:"+vcitem.getName());
                   System.out.println("vcitemvalue in vcrow:"+vcitem.getValue());
               
         }

}

//Add the row to the existing Criteria
vc.addElement(criteriaRow);
ADF : Conditional Render in ADF Faces/ Manually creating ADF Table display
Use case :

The Employee of a company fills an online form of resigning reason before leaving the organization.
All these comments/reason submitted by various employees who left the organization will be saved in a separate DB table Job History.

Now the list of employees with the feedback  in the UI looks like this


Note: I am constructing these information as a table format without using ADF Table . The reason is to explain the scenario with more of real time case.

To achieve this we can use af:gridrow that can loop over the rows that the iterator has.

<af:iterator value="#{bindings.JobHistory.collectionModel}"    var="row" id="it2"   >
           //Code to loop over the rows
         <af:gridRow>
        <af:gridCell>

        </af:gridCell>
       </af:gridRow>
<af:iterator/>

It just looks like the old jsp code where in the table TR and TD loops over a collection of data.
we generally fix the cell property of the row to fixed to have the limited alignment of the table in the page. The space between each row should be dynamic so that the text in the cells would not get overlapped.

something like this will makes sense

<af:iterator value="#{bindings.JobHistory.collectionModel}"    var="row" id="it2"   >
<af:gridRow id="gr3"height="#{row.comments gt 100 ? '40px' : '20px'}">
 <af:gridCell marginStart="10px" width="100px" id="gc7"       valign="stretch" halign="stretch">
              <af:outputText value="#{row.Name}" id="ot8" noWrap="false"/>
            </af:gridCell>
            <af:gridCell marginStart="10px" id="gc9" width="250px"   valign="stretch" halign="stretch">
              <af:outputText value="#{row.comments}" id="ot10"     inlineStyle="word-wrap:break-word" noWrap="false"/>
            </af:gridCell>
 </af:gridRow>

Also few more EL expressions for the conditional rendering of the ADF Faces components

<af:outputText value="#{pageFlowScope.data.request eq 'YES' and pageFlowScope.data.response eq 'N' ? 'DONE': 'COMPLETED'}"  id="ot3"/>

 <af:outputText value="#{str}" id="ot1" rendered="#{requestContext.flag=='YES'}" />

<af:commandLink id="ci2"     rendered="#{row.flag == 'Y' }"  />
How to create custom CSS file for ADF Web application
How to  create custom CSS  file for ADF Web Application

Here is the step by step process .. Before that create a view controller project in the ADF Web Application

1) create trinidad-skins.xml(If you don't find ) in this path (..\ViewController\public_html\WEB-INF\trinidad-skins.xml)
 
<?xml version="1.0" encoding="windows-1252" ?>
<skins xmlns="http://myfaces.apache.org/trinidad/skin">
  <skin>
     <id>MyOwn.desktop</id>
    <family>MyOwn</family>
    <extends>blafplus-rich.desktop</extends>
    <render-kit-id>org.apache.myfaces.trinidad.desktop</render-kit-id>
    <style-sheet-name>css/sample.css</style-sheet-name>
  </skin>
</skins>

2)trinidad-config.xml

<?xml version="1.0" encoding="windows-1252"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <skin-family>MyOwn</skin-family>
</trinidad-config>


2) CSS file is in the path
    \ViewController\public_html\css\sample.css
 
 
3) The below property can be set in the view controller project web.xml. This will check the automatic changes in your css file and reflect in the Adf UI after refresh.
   This should not be used in Production environment..    By Default this property is set to false.
<context-param>
    <description>If this parameter is true, there will be an automatic check of the modification date of your JSPs, and saved state will be discarded when JSP's change. It will also automatically check if your skinning css files have changed without you having to restart the server. This makes development easier, but adds overhead. For this reason this parameter should be set to false when your application is deployed.</description>
    <param-name>org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION</param-name>
    <param-value>true</param-value>
  </context-param>


Thats it !!! Its done.

Test it with the below steps

1)content in sample.css
 
    af|commandButton {
        color: red;
        font-weight: bold;
        font-size: 12px;
        text-decoration: none;
    }
2)create a button in jspx page and run  .
    <af:commandButton text="Search" id="cb1"/>

No comments:

Post a Comment