Friday, June 5, 2015

How to implement 100% Dynamic ADF table with inputText having value change listeners.

  I have a use case that has the following business requirements: 

    1) Have multiple database tables and display them on the page; 
   2) The application needs to have the capability to track end user changes;
  
  The easiest way in ADF is to create the number of Entity Objects and same number of View Objects. Then user needs to create an ADF page and drag drops the same number of times on the page. After that user needs to have a drop down to allow end user to choose which table to work on and based on this selection the corresponding table will be rendered and all the others will be hidden. User needs to define a value change listener for every field for each table.
  This solution seems OK for a handful of tables in the application, how about the developer needs to work with a subset of Oracle E-Business Suite Inventory module tables which can be several tens of thousands of tables!
  There is a better solution. Use dynamic view, dynamic tables. Along with my implementation, I referred to the following blog post: Eugene Fedorenko's ADF Practice blog - "Dynamic Table Declarative Component" and Andrejus Baranovskis Blog - "ADF Generator for Dynamic ADF BC and ADF UI". Engene and Andrejus has given a working sample respectively. My job is to understand the code and put them together, extend and tweak to meet my specific requirements.
  The following are the break-down of the design and implementation steps towards the completion of finishing this application.

 Choose refreshing mechanism
  No matter what approach you select, you will have a drop down containing all the table names. Then after each selection the table display area needs to be refreshed. One can put everything on a single page and use ppr appropriately to refresh. Or one can put table display area in a region. Such region has a Bounded Task Flow behind it and the BTF has its refresh set to 'ifNeeded' in page definition file and when its parameters change, the BTF will be automatically refreshed. In my sample I am using the latter.

 Dynamically construct view object
  The constructVOInstance() method in AppModuleImpl.java file is the one which construct the view object at run time on the fly. It retrieves all the Entity Object's attribute names and define the view definition file on top of them, then calls createViewObject() to create the view object.

         ViewDefImpl viewDef = new ViewDefImpl(viewDefName);
            viewDef.addEntityUsage(shortEoName, eoName, false, false);
            viewDef.addAllEntityAttributes(shortEoName);            

            String select = ViewDefImpl.buildDefaultSelect(viewDef.getAttributeDefs());
            viewDef.setSelectClause(select);

            viewDef.resolveDefObject();
            viewDef.registerDefObject();

            String from = viewDef.buildDefaultFrom();
            viewDef.setFromClause(from);
            System.out.println("vo Name : " + voName);

            ViewObject vo = createViewObject(voName, viewDef);


 Build your dynamic table on page
   Developer needs to drag the DummyVO onto his/her page/fragment and drops it as:
    a) ADF dynamic table with dynamic:table tag; 
    b) ADF dynamic table with af:table tag; 

I later found out that dynamic:table is not applicable in my case because developer cannot add anything inside it. So here is what I am using on my page fragment as the definition of ADF table:
    <af:table value="#{pageFlowScope.TableBean.tree.collectionModel}" var="row" rows="#{pageFlowScope.TableBean.tree.rangeSize}"
                      emptyText="#{pageFlowScope.TableBean.tree.viewable ? 'No data to display.' : 'Access Denied.'}"  fetchSize="#{pageFlowScope.TableBean.tree.rangeSize}" rowBandingInterval="0" id="t2">
                

                <af:forEach items="#{pageFlowScope.currentViewDefs}" var="def">
                <af:column sortProperty="#{def.name}" sortable="false" headerText="#{def.name}" id="c1">
                        <af:inputText value="#{row[def.name]}" id="it2" autoSubmit="true"
                                      valueChangeListener="#{mainBean.fieldValueChange}"/>
                    </af:column>
                </af:forEach>
            </af:table>

  We will deal with the tricky part later, but basically I am achieving dynamic by embed af:forEach underneath af:table to get dynamic.

  Notice that the "value" attribute has something like: #{pageFlowScope.TableBean.tree.collectionModel}. This is one of the tricky part. The dynamic is coming from the logic in backing bean getTree() method.

 Construct JUCtrlHierBinding dynamically
   You see what is in the getTree() method in TableBackingBean class.
       JUCtrlHierBinding chb = (JUCtrlHierBinding)dcb.findCtrlBinding(TREE_NAME);

        if (chb != null) {
            dcb.removeControlBinding(TREE_NAME);
        }

        //Looking for the VEmpIterator iterator
        JUIteratorBinding iter = (JUIteratorBinding)getIterator(ITERATOR_NAME, VO_NAME);

        //Create and init a tree binding definition
        JUCtrlHierDef hierDef = new FacesCtrlHierDef();
        HashMap initValues = new HashMap();
        initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName());
        JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding();
        initValues.put(JUCtrlHierDef.PNAME_TypeBindings, new JUCtrlHierTypeBinding[] { typeBinding });
        hierDef.init(initValues);

        //Create a tree binding instance
        chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb);

        //Add the instance to the current binding container
        dcb.addControlBinding(TREE_NAME, chb);   

   Same as when constructing ViewObject dynamically, when you try to construct the JUCtrlHierBinding, you need to construct its Defition first.
  You may already know that the corresponding Java class for af:table is RichTable and inside it there is something called wrapped data, which is the collectionmodel normally in af:table's value attribute. And it is the data set for af:table's var attribute to iterator through such data set. This wrapped data or collectionmodel has its corresponding Java class too which is JUCtrlHierBinding. These are the page UI layer objects, and at the page definition layer there is Iterator Binding or JUIteratorBinding Java class. And down the chain at the ADF Model layer it is ViewObject in Application Module or ViewObjectImpl Java class. These are pretty much what Oracle ADF data binding is all about and how the data flows during data binding. 
  Lastly I drag drop the TableNamesVO onto my main page as a Select One Choice. This view object contains the SQL query for all the table names that need to be managed in this application. When the value of this drop down changes, the table name will be the parameter for the table display area bounded task flow and cause it to be processed as illustrated in 'Dynamically construct view object' section.
  In this sample I am using Oracle's scott schema. There are 4 tables from scott schema will be shown in the table selection drop down. In order to correctly run the sample application, please make sure scott schema is unlocked and is connectable. See the following screen shot for the resulting page:
 
  If you need to add another 100 tables to be managed for this application, all you need to do is make sure TableNamesVO SQL has the new 100 table names, that's it!
  These are all you need to know in order to have a very dynamic, flexible, extensible and sustainable approach to achieve 100% Oracle dynamic table.
  Please find the working sample code below in the attachment of this post. Enjoy! 

 DynamicEditableAFTable.rar
 

No comments:

Post a Comment