Adding Dynamic Attribute in View Object and Showing in ADF Dynamic Table

Hi All,

Today I am going to show you how you can create the dynamic attribute in existing view object and how you can show it in the dynamic table.

In my scenario there was two attribute "FirstName" and "LastName" was static and already present in the view object. Now I need to create the two extra dynamic column  suppose "Email" and "Age" in the existing view object.

First of all I have created a transient view object with two attribute "FirstName" and "LastName" as shown below:


Now I have attached this view object in application module as shown below:


Now open you view object and go to "Java" section and generate the VOImpl class as shown below:


Also generate the ROWImpl class as shown below:


Once VOImpl and VORowImpl class is generated then write the following code inside your VOImpl class.

package model;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import model.common.TestTransientVO;
import oracle.jbo.NameValuePairs;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.server.AttributeDefImpl;
import oracle.jbo.server.ViewObjectImpl;
import oracle.jbo.server.ViewRowImpl;
import oracle.jbo.server.ViewRowSetImpl;

public class TestTransientVOImpl extends ViewObjectImpl implements TestTransientVO {
    private List<NameValuePairs> rowData = null;
        private boolean initialized = false;
        int rows = 0;
    /**
     * This is the default constructor (do not remove).
     */
    public TestTransientVOImpl() {
    }
    /**
         * executeQueryForCollection - overridden for custom java data source support.
         */
        protected void executeQueryForCollection(Object qc, Object[] params, int noUserParams) {
            if (!initialized) {
                initialized = true;
            }
            setFetchPos(qc, 0);
            super.executeQueryForCollection(qc, params, noUserParams);
        }
   
    /**
        * hasNextForCollection - overridden for custom java data source support.
        */
       protected boolean hasNextForCollection(Object qc) {
           return getFetchPos(qc) < rows;
       }
   
    /**
         * createRowFromResultSet - overridden for custom java data source support.
         */
        protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet resultSet) {
            if (rowData != null) {
                TestTransientVORowImpl r = (TestTransientVORowImpl)createNewRowForCollection(qc);
                int pos = getFetchPos(qc);
                NameValuePairs attrs = rowData.get(pos);
                String[] attrNames = attrs.getAttributeNames();
                for (int i = 0; i < attrNames.length; i++) {
                    Object attrValue = attrs.getAttribute(attrNames[i]);
                    if (this.getAttributeIndexOf(attrNames[i]) >= 0) {
                        this.populateAttributeForRow(r, this.getAttributeIndexOf(attrNames[i]), attrValue);
                    } else {
                        AttributeDefImpl dyCol1 =
                            (AttributeDefImpl)this.addDynamicAttributeWithType(attrNames[i], "java.lang.String", null);
                        dyCol1.setQueriable(true);
                        r.setAttribute(dyCol1.getName(), attrValue);
                    }
                }
                setFetchPos(qc, pos + 1);
                System.out.println(r.getAttributeCount());
                return r;
            } else {
                setFetchPos(qc, 0);
                return null;
            }
        }

        /**
         * @param rowset
         * @param pos
         */
        private void setFetchPos(Object rowset, int pos) {
            if (pos == rows) {
                setFetchCompleteForCollection(rowset, true);
            }
            setUserDataForCollection(rowset, new Integer(pos));
        }

        /**
         * @param rowset
         * @return
         */
        private int getFetchPos(Object rowset) {
            return ((Integer)getUserDataForCollection(rowset)).intValue();
        }

        /**
         * getQueryHitCount - overridden for custom java data source support.
         * @param viewRowSet
         * @return
         */
        public long getQueryHitCount(ViewRowSetImpl viewRowSet) {
            return rows;
        }

        public void intialiseRowData() {
            if (rowData == null) {
                rowData = new ArrayList<NameValuePairs>();
            } else {
                rowData.clear();
            }
        }

        public void setCountRowData() {
            rows = (rowData != null) ? rowData.size() : 0;
        }
       
     /**

    * This method is used to populate the view object with all the attribute.
    */  
    public void populateTable() {
           intialiseRowData();
           rows = 0;
                   NameValuePairs nvp = new NameValuePairs();
                   rows = rows + 1;
           nvp.setAttribute("FirstName", "Kunal");
           nvp.setAttribute("LastName", "Kumar");
           nvp.setAttribute("Email", "kumar.kumar2808@gmail.com");
           nvp.setAttribute("Age", 29);
                  
                   rowData.add(nvp);
                   System.out.println("Row data called > "+rowData);
           setCountRowData();
           executeQuery();
       }

}




Now expose the populateTable() method in client interface by following the steps shown below:


Once you have exposed the method in client interface you can see the following java files in the view object's java section.



Now create the JSPX/JSFF page in your View Controller project and drag and drop the method of the client interface as "Command Button" as shown below:


Now drag and drop the view object instance from Data Control section on to the page as "ADF Read Only Dynamic Table" as shown below:


Now give the partial trigger for the table from the command button as shown below:


At this point your jspx code will look like this:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1">
      <af:messages id="m1"/>
      <af:form id="f1">
        <af:commandButton actionListener="#{bindings.populateTable.execute}"
                          text="populateTable"
                          disabled="#{!bindings.populateTable.enabled}"
                          id="cb1"/>
        <af:table rows="#{bindings.TestTransientVO1.rangeSize}"
                  fetchSize="#{bindings.TestTransientVO1.rangeSize}"
                  emptyText="#{bindings.TestTransientVO1.viewable ? 'No data to display.' : 'Access Denied.'}"
                  var="row" rowBandingInterval="0"
                  value="#{bindings.TestTransientVO1.collectionModel}"
                  selectedRowKeys="#{bindings.TestTransientVO1.collectionModel.selectedRow}"
                  selectionListener="#{bindings.TestTransientVO1.collectionModel.makeCurrent}"
                  rowSelection="single" id="t1" partialTriggers="::cb1" columnStretching="last" styleClass="AFStretchWidth">
          <af:forEach items="#{bindings.TestTransientVO1.attributeDefs}"
                      var="def">
            <af:column headerText="#{bindings.TestTransientVO1.labels[def.name]}"
                       sortable="true" sortProperty="#{def.name}" id="c1">
              <af:outputText value="#{row[def.name]}" id="ot1"/>
            </af:column>
          </af:forEach>
        </af:table>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>


Now run your JSPX/JSFF page and see what output is coming on the page after clicking on the button.

So after clicking on the button we will get the following picture.


So as you can noticed here that we are not able to see the dynamic column which it suppose to show.

So to overcome this issue we have modified the dynamic table code as shown below:

  <af:table rows="#{bindings.TestTransientVO1.rangeSize}"
                  fetchSize="#{bindings.TestTransientVO1.rangeSize}"
                  emptyText="#{bindings.TestTransientVO1.viewable ? 'No data to display.' : 'Access Denied.'}"
                  var="row" rowBandingInterval="0"
                  value="#{bindings.TestTransientVO1.collectionModel}"
                  selectedRowKeys="#{bindings.TestTransientVO1.collectionModel.selectedRow}"
                  selectionListener="#{bindings.TestTransientVO1.collectionModel.makeCurrent}"
                  rowSelection="single" id="t1" partialTriggers="::cb1"
                  columnStretching="last" styleClass="AFStretchWidth">
          <af:forEach items="#{bindings.TestTransientVO1Iterator.attributeDefs}"
                      var="def">
            <af:column headerText="#{bindings.TestTransientVO1.labels[def.name]}"
                       sortable="true" sortProperty="#{def.name}" id="c1">
              <af:outputText value="#{row[def.name]}" id="ot1"/>
            </af:column>
          </af:forEach>
        </af:table>

Now if we run the page and see we will get the screen as shown below:


So as you can see that the dynamic column is getting added but we are not able to see the header text of the column.

Now to overcome this issue we have to modify the tree binding of the table in page definition file.

When you open the tree binding you can see that the two attribute is shuffled in the right side as shown below:


So to make it work we have to shuffle back these two attribute on left side and then click on "OK" button as shown below:


Now run your page and see the result. You can see the dynamic column is added with the values in the table after clicking on the "populateTable" command button as shown below:


So here you may arise following question

1. Why default ADF Dynamic table didn't worked as expected.
2. Why we need to modify the af:forEach tag item property to point it to iterator instead of tree
     binding.
3. Why after removing the attribute from tree binding in page definition file only it worked perfectly.


So I would say frankly, I don't know why ADF is behaving like this in all these scenarios. This might be ADF bug also but I am not sure. But this is the work around which I found to make it work perfectly.


So just cheers and Happy Coding :)

Comments

  1. Thanks Kunal for such a wonderful post.. I struggled for hours to find out the solution, finally I got my answer.. Keep it up !!!

    ReplyDelete
  2. Hi Kunal Kumar,

    Many thanks , I tried this blog which is working fine for my requirement but one issue i found out is in this sorting is not working for the dynamic columns given error like

    "( ADF: Adding the following JSF error message: Definition Email of type Attribute is not found in TestTransientVO1.
    oracle.jbo.NoDefException: JBO-25058: Definition Email of type Attribute is not found in TestTransientVO1.)"

    can you please provide any suggestion to resolve this issue ...

    Thanks ,
    Virendra

    ReplyDelete

Post a Comment

Popular posts from this blog

Setting up the environment for Angular2 and Hello World Example in Angular2

Showing number of rows or row count on top and bottom of table in ADF.

Build a Simple ReactJS application using react-cli