Monday, March 28, 2011

Using Region Capabilities API


 Assume the following task flow:




And now assume that we need to show the navigation available from the present view activity in a choice list, as shown below:



The question is how to do this?

Solution:

We need to use Capabilities API available in java to populate the choice list as shown below:

public SelectItem[] getSelectItems(){
  RegionModel rm = getRegion().getRegionModel();
  Set<String> capabilities = rm.getCapabilities();
    
  SelectItem[] si = new SelectItem[capabilities.size()];
  Object[] s = capabilities.toArray();

  for ( int i = 0; i < s.length; i++ ){
      option = s[i].toString();
      si[i] = new SelectItem(option);
  }
  return si;
}

You can refer that from the SelectOneChoice component as shown below:

        <af:selectOneChoice label="Navigations Available" id="soc1" ...>
          <f:selectItems value="#{Bean.selectItems}"
                         id="si1"/>
        </af:selectOneChoice>


Note:
The EL equivalent to use the capabilities is shown as below:

"#{bindings.EmpDeptTaskFlow1.regionModel.capabilities['Department']}"

The above EL expression figures out if the region is having Department as an available navigation capability from the current view activity.

That's it.

JDev Release 11.1.1.4

Thursday, March 24, 2011

How to use the Carousel Component.

There are two things you need to know about using Carousel:

  1. Include the images in out JDev project itself instead of storing it in the database because otherwise you need to fetch the image yourself from the database as Carousel component as of now does not do it automatically.
  2. Prefix the binding with the ‘item’ variable.

The following image shows how to copy the image to your project:



I used the following table to test the Carousel component:

CREATE TABLE image_table (
  image_id    NUMBER(15,0)  PRIMARY KEY,
  description VARCHAR2(20)  NULL,
  url         VARCHAR2(200) NULL
);

INSERT INTO IMAGE_TABLE VALUES (1, 'Image 1', 'Cloud1.jpg');
INSERT INTO IMAGE_TABLE VALUES (2, 'Image 2', 'Cloud2.jpg');
INSERT INTO IMAGE_TABLE VALUES (3, 'Image 3', 'Cloud3.jpg');

Just stored the name of the images in the database, and copied the images to my UI Project.

Here comes the code of the page that contains the Carousel component, with key points highlighted:

<?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:carousel currentItemKey="#{bindings.ImageTableVO1.treeModel.rootCurrencyRowKey}"
                     value="#{bindings.ImageTableVO1.treeModel}" var="item"

          <f:facet name="nodeStamp">
            <af:carouselItem id="ci1"
                             text="Description : #{item.bindings.Description.inputValue}">
              <af:outputText value="Image Id: #{item.bindings.ImageId.inputValue}" id="ot1">
                <af:convertNumber groupingUsed="false"
                                  pattern="#{bindings.ImageId.format}"/>
              </af:outputText>
              <af:spacer width="10" height="10" id="s1"/>
              <af:separator id="s2"/>
              <af:image source="/images/#{item.bindings.Url.inputValue}" id="i1"/>

          </f:facet>
        </af:carousel>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>

And here is the page definition content:

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                version="11.1.1.59.23" id="CarouselPagePageDef"
                Package="view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables"/>
    <iterator Binds="ImageTableVO1" RangeSize="25"
              DataControl="AppModuleDataControl" id="ImageTableVO1Iterator"/>
  </executables>
  <bindings>
    <tree IterBinding="ImageTableVO1Iterator" id="ImageTableVO1">
      <nodeDefinition DefName="model.ImageTableVO">
        <AttrNames>
          <Item Value="ImageId"/>
          <Item Value="Description"/>
          <Item Value="Url"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
    <attributeValues IterBinding="ImageTableVO1Iterator" id="ImageId">

      <AttrNames>
        <Item Value="Description"/>
      </AttrNames>
    </attributeValues>
  </bindings>
</pageDefinition>

Here is how the component looked in the browser:




That's it.

JDev Release 11.1.1.4

Region Interaction Example 4 - Using Parent Action Component

Assume the following UI:


Here, the top region before the separator represents the parent task flow, and the region below the separator represents the child task flow. In the child task flow, on clicking the ‘Move Parent’ button, the parent task flow navigation is performed, as shown below:



However, on clicking the ‘Move Child’ button, the navigation in the child task flow is performed, as shown in below image:



To perform this type of navigation, Parent Action component is used. Parent action component is used in the child task flow to perform the navigation in the parent task flow. The following image shows the parent task flow used in the above specified flow:


The ParentView.jsff refers to the child task flow and the following image shows the content of the child task flow:


The files used in this flow are:


Here is the code of the above specified file used in the flow:
MasterPage.jspx
<?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:form id="f1">
        <af:region value="#{bindings.ParentTaskFlow1.regionModel}" id="r1"/>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>
ParentTaskFlow.xml
<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="ParentTaskFlow">
    <default-activity id="__1">ParentView.jsff</default-activity>
    <view id="ParentView.jsff">
      <page>/ParentView.jsff</page>
    </view>
    <view id="ParentView2.jsff">
      <page>/ParentView2.jsff</page>
    </view>
    <control-flow-rule id="__2">
      <from-activity-id id="__3">ParentView.jsff</from-activity-id>
      <control-flow-case id="__5">
        <from-outcome id="__6">move</from-outcome>
        <to-activity-id id="__4">ParentView2.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__7">
      <from-activity-id id="__8">ParentView2.jsff</from-activity-id>
      <control-flow-case id="__10">
        <from-outcome id="__11">back</from-outcome>
        <to-activity-id id="__9">ParentView.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>
ParentView.jsff
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <p>
    <af:outputText value="First View In Parent Task Flow" id="ot1"/>
    <af:commandButton text="Move" id="cb1" action="move"/>
  </p>
  <af:separator id="s1"/>
  <af:region value="#{bindings.ChildTaskFlow1.regionModel}" id="r1"/></jsp:root>
ParentView.jsff
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:outputText value="Second View In Parent Task Flow" id="ot1"/>
  <af:commandButton text="Back" id="cb1" action="back"/>
</jsp:root>
ChildTaskFlow.xml
<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="ChildTaskFlow">
    <default-activity id="__1">ChildRegion1.jsff</default-activity>
    <managed-bean id="__37">
      <managed-bean-name id="__35">ChildRegion1Bean</managed-bean-name>
      <managed-bean-class id="__36">ChildRegion1Bean</managed-bean-class>
      <managed-bean-scope id="__38">request</managed-bean-scope>
    </managed-bean>
    <view id="ChildRegion1.jsff">
      <page>/ChildRegion1.jsff</page>
    </view>
    <view id="ChildRegion2.jsff">
      <page>/ChildRegion2.jsff</page>
    </view>
    <parent-action id="parentAction1">
      <parent-outcome>move</parent-outcome>
      <outcome id="__24">next</outcome>
    </parent-action>
    <parent-action id="parentAction2">
      <parent-outcome>move1</parent-outcome>
      <outcome id="__33">next</outcome>
    </parent-action>
    <control-flow-rule id="__2">
      <from-activity-id id="__3">ChildRegion2.jsff</from-activity-id>
      <control-flow-case id="__4">
        <from-outcome id="__6">back</from-outcome>
        <to-activity-id id="__5">ChildRegion1.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__12">
      <from-activity-id id="__13">ChildRegion1.jsff</from-activity-id>
      <control-flow-case id="__18">
        <from-outcome id="__25">move1</from-outcome>
        <to-activity-id id="__17">parentAction1</to-activity-id>
      </control-flow-case>
      <control-flow-case id="__26">
        <from-outcome id="__28">move2</from-outcome>
        <to-activity-id id="__27">parentAction2</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__19">
      <from-activity-id id="__20">parentAction1</from-activity-id>
      <control-flow-case id="__22">
        <from-outcome id="__23">next</from-outcome>
        <to-activity-id id="__21">ChildRegion2.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__29">
      <from-activity-id id="__30">parentAction2</from-activity-id>
      <control-flow-case id="__31">
        <from-outcome id="__34">next</from-outcome>
        <to-activity-id id="__32">ChildRegion2.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>
ChildRegion1.jsff
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:outputText value="First Region Of The Child Task Flow" id="ot1"/>
  <af:commandButton text="Move Parent" id="cb1" action="move1"/>
  <af:commandButton text="Move Child" id="cb2" action="move2"/>
</jsp:root>


ChildRegion2.jsff
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:outputText value="Second Region Of The Child Task Flow" id="ot1"/>
  <af:commandButton text="Back" id="cb1" action="back"/>
</jsp:root>

That's it.

JDev Release 11.1.1.4

Sunday, March 20, 2011

Region Interaction Example 3 - Using Queue Action Event API

Scenario:
Imagine the following UI:



What we need to do is:
  1. Sync up the right task flow/region with the node selected in the left side tree.
  2. Enable/Disable the Employee/Department command button based on the visible table. For example, if Department table is visible than Employee command button should be visible and vice versa.
  3. Switch the region based on the Employee/Command button clicked.
The following figure explains the scenario:


And after clicking on Departments button, we get:



Constraint:

The database tables are independent of each other as shown below:

CREATE TABLE EMP (
  EMP_ID      NUMBER(10,0) PRIMARY KEY,
  EMP_NAME    VARCHAR2(50) NULL,
  DEPT_ID     NUMBER(10,0) NULL,
  LOCATION_ID NUMBER(4,0)  NULL
);

INSERT INTO EMP VALUES ( 1, 'Emp 1', 1, 1 );
INSERT INTO EMP VALUES ( 2, 'Emp 2', 2, 2 );
INSERT INTO EMP VALUES ( 3, 'Emp 3', 3, 3 );
INSERT INTO EMP VALUES ( 4, 'Emp 4', 4, 4 );
INSERT INTO EMP VALUES ( 5, 'Emp 5', 5, 5 );
INSERT INTO EMP VALUES ( 6, 'Emp 6', 1, 1 );
INSERT INTO EMP VALUES ( 7, 'Emp 7', 2, 2 );
INSERT INTO EMP VALUES ( 8, 'Emp 8', 3, 3 );
INSERT INTO EMP VALUES ( 9, 'Emp 9', 4, 4 );
INSERT INTO EMP VALUES ( 10, 'Emp 10', 5, 5 );

CREATE TABLE DEPT (
  DEPT_ID     NUMBER(10,0) PRIMARY KEY,
  DEPT_NAME   VARCHAR2(50) NULL,
  LOCATION_ID NUMBER(4,0)  NULL
);

INSERT INTO DEPT VALUES ( 1, 'Finance', 1 );
INSERT INTO DEPT VALUES ( 2, 'Sales', 2 );
INSERT INTO DEPT VALUES ( 3, 'Human Resource', 3 );
INSERT INTO DEPT VALUES ( 4, 'BPO', 4 );
INSERT INTO DEPT VALUES ( 5, 'SCM', 5 );

CREATE TABLE LOCATIONS (
  LOCATION_ID    NUMBER(4,0)  PRIMARY KEY ,
  STREET_ADDRESS VARCHAR2(40) NULL
);

INSERT INTO LOCATIONS VALUES (1, 'Location 1');
INSERT INTO LOCATIONS VALUES (2, 'Location 2');
INSERT INTO LOCATIONS VALUES (3, 'Location 3');
INSERT INTO LOCATIONS VALUES (4, 'Location 4');
INSERT INTO LOCATIONS VALUES (5, 'Location 5');

Solution:

1. For tree table node selection, apart from node selection row key setting, you need to refresh the right region programatically.
2. Set the command button disabled property by determining region capabilities.
3. Navigate to the appropriate view using Queueing Action Event API.

Task flow involved for the right region is shown below:


Pasting the code to speak for itself. Key points highlighted. Three files involved (apart from the data model files) majorly.

QueueActionEvent.jspx

<?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"
                 binding="#{backingBeanScope.backing_QueueActionEvent.d1}">
      <af:messages binding="#{backingBeanScope.backing_QueueActionEvent.m1}"
                   id="m1"/>
      <af:form id="f1"
               binding="#{backingBeanScope.backing_QueueActionEvent.f1}">
        <af:panelSplitter binding="#{backingBeanScope.backing_QueueActionEvent.ps1}"
                          id="ps1">
          <f:facet name="first">
            <af:tree value="#{bindings.LocationVO1.treeModel}" var="node"
                     selectionListener="#{backingBeanScope.backing_QueueActionEvent.onSelection}"
                     rowSelection="single"
                     binding="#{backingBeanScope.backing_QueueActionEvent.t1}"
                     id="t1">
              <f:facet name="nodeStamp">
                <af:outputText value="#{node}"
                               binding="#{backingBeanScope.backing_QueueActionEvent.ot1}"
                               id="ot1"/>
              </f:facet>
            </af:tree>
          </f:facet>
          <f:facet name="second">
            <af:panelGroupLayout layout="scroll"
                                 xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                                 binding="#{backingBeanScope.backing_QueueActionEvent.pgl1}"
                                 id="pgl1">
              <af:region value="#{bindings.EmpDeptTaskFlow1.regionModel}"
                         id="r1"
                         binding="#{backingBeanScope.backing_QueueActionEvent.r1}"
                         partialTriggers="::t1 ::cb1 ::cb2"/>
              <af:commandButton text="Employees"
                                binding="#{backingBeanScope.backing_QueueActionEvent.cb1}"
                                id="cb1"
                                actionListener="#{backingBeanScope.backing_QueueActionEvent.actionListenerEmpSubmit}"
                                partialSubmit="true"
                                disabled="#{bindings.EmpDeptTaskFlow1.regionModel.capabilities['Department']}"
                                partialTriggers="cb2"/>
              <af:commandButton text="Departments"
                                binding="#{backingBeanScope.backing_QueueActionEvent.cb2}"
                                id="cb2"
                                actionListener="#{backingBeanScope.backing_QueueActionEvent.actionListenerDeptSubmit}"
                                partialSubmit="true"
                                disabled="#{bindings.EmpDeptTaskFlow1.regionModel.capabilities['Employee']}"
                                partialTriggers="cb1"/>
            </af:panelGroupLayout>
          </f:facet>
        </af:panelSplitter>
      </af:form>
    </af:document>
  </f:view>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_QueueActionEvent-->
</jsp:root>

Backing Bean QueueActionEvent.java

package view.backing;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.PhaseId;
import oracle.adf.view.rich.component.rich.fragment.RichRegion;
import org.apache.myfaces.trinidad.event.SelectionEvent;

public class QueueActionEvent {
    private RichRegion r1;
    private String navigate;
   
    public void actionListenerEmpSubmit(ActionEvent ae){
      this.navigate = "Employee";
      navigateWithinRegion();
    }
   
    public void actionListenerDeptSubmit(ActionEvent ae){
      this.navigate = "Department";
      navigateWithinRegion();
    }
   
    private void navigateWithinRegion(){
      FacesContext fctx = FacesContext.getCurrentInstance();
      ELContext elctx = fctx.getELContext();
      ExpressionFactory expressionFactory = fctx.getApplication().getExpressionFactory();
      MethodExpression methodExpresion = expressionFactory.createMethodExpression(
                                             elctx, navigate,
                                             String.class, new Class[]{}
                                             );
      r1.queueActionEventInRegion( methodExpresion, null, null, false, 0, 0, PhaseId.INVOKE_APPLICATION );
    }
    public void setNavigate(String navigate) {
        this.navigate = navigate;
       
    }
    public String getNavigate() {
        return navigate;
    }
    public void onSelection(SelectionEvent selectionEvent){
      FacesContext fctx = FacesContext.getCurrentInstance();
      ELContext elctx = fctx.getELContext();
      ExpressionFactory expressionFactory = fctx.getApplication().getExpressionFactory();
      MethodExpression methodExpresion = expressionFactory.createMethodExpression(
                                             elctx, "#{bindings.LocationVO1.treeModel.makeCurrent}",
                                             Object.class, new Class[]{selectionEvent.getClass()}
                                             );
      methodExpresion.invoke(elctx, new Object[]{selectionEvent});
      r1.refresh(fctx);
    }
    public void setR1(RichRegion r1) {
        this.r1 = r1;
    }
    public RichRegion getR1() {
        return r1;
    }
}

AMImpl Code

package model;
import model.common.AppModule;
import oracle.jbo.ViewObject;
import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.ViewObjectImpl;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Sat Mar 19 20:43:03 IST 2011
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class AppModuleImpl extends ApplicationModuleImpl implements AppModule {
    /**
     * This is the default constructor (do not remove).
     */
    public AppModuleImpl() {
    }
    /**
     * Container's getter for EmpVO1.
     * @return EmpVO1
     */
    public ViewObjectImpl getEmpVO1() {
        return (ViewObjectImpl)findViewObject("EmpVO1");
    }
    /**
     * Container's getter for DeptVO1.
     * @return DeptVO1
     */
    public ViewObjectImpl getDeptVO1() {
        return (ViewObjectImpl)findViewObject("DeptVO1");
    }
   
    public void executeDeptVOUsingLocation(oracle.jbo.domain.Number locId){
      ViewObject vo = this.getDeptVO1();
      // Set the two design time named bind variables
      vo.setWhereClause("location_id = :TheLocId");
      vo.defineNamedWhereClauseParam("TheLocId", null, null);
      vo.setNamedWhereClauseParam("TheLocId", locId);
      vo.executeQuery();
    }

    public void executeEmpVO(oracle.jbo.domain.Number deptId){
      ViewObject vo = this.getEmpVO1();
      // Set the two design time named bind variables
      vo.setWhereClause("dept_id = :TheDeptId");
      vo.defineNamedWhereClauseParam("TheDeptId", null, null);
      vo.setNamedWhereClauseParam("TheDeptId", deptId);
      vo.executeQuery();
    }
   
    public void executeEmpVOUsingLocation(oracle.jbo.domain.Number locId){
      ViewObject vo = this.getEmpVO1();
      // Set the two design time named bind variables
      vo.setWhereClause("location_id = :TheLocId");
      vo.defineNamedWhereClauseParam("TheLocId", null, null);
      vo.setNamedWhereClauseParam("TheLocId", locId);
      vo.executeQuery();
    }   
    /**
     * Container's getter for LocationVO1.
     * @return LocationVO1
     */
    public ViewObjectImpl getLocationVO1() {
        return (ViewObjectImpl)findViewObject("LocationVO1");
    }
}

Task Flow Source Code:

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="EmpDeptTaskFlow">
    <default-activity id="__2">executeEmpVOUsingLocation</default-activity>
    <input-parameter-definition id="__7">
      <name id="__8">locationId</name>
      <value>#{pageFlowScope.locId}</value>
      <class>oracle.jbo.domain.Number</class>
    </input-parameter-definition>
    <managed-bean id="__6">
      <managed-bean-name id="__3">backing_DeptView</managed-bean-name>
      <managed-bean-class id="__4">view.backing.DeptView</managed-bean-class>
      <managed-bean-scope id="__5">backingBean</managed-bean-scope>
      <!--oracle-jdev-comment:managed-bean-jsp-link:1DeptView.jsff-->
    </managed-bean>
    <view id="DeptView.jsff">
      <page>/DeptView.jsff</page>
    </view>
    <method-call id="executeDeptVOUsingLocation">
      <method>#{bindings.executeDeptVOUsingLocation.execute}</method>
      <outcome id="__9">
        <fixed-outcome>executeDeptVOUsingLocation</fixed-outcome>
      </outcome>
    </method-call>
    <method-call id="executeEmpVOUsingLocation">
      <method>#{bindings.executeEmpVOUsingLocation.execute}</method>
      <outcome id="__20">
        <fixed-outcome>executeEmpVOUsingLocation</fixed-outcome>
      </outcome>
    </method-call>
    <view id="EmplView">
      <page>/EmplView.jsff</page>
    </view>
    <control-flow-rule id="__15">
      <from-activity-id id="__16">executeDeptVOUsingLocation</from-activity-id>
      <control-flow-case id="__17">
        <to-activity-id id="__18">DeptView.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__21">
      <from-activity-id id="__22">DeptView.jsff</from-activity-id>
      <control-flow-case id="__24">
        <from-outcome id="__25">Employee</from-outcome>
        <to-activity-id id="__23">executeEmpVOUsingLocation</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__32">
      <from-activity-id id="__33">executeEmpVOUsingLocation</from-activity-id>
      <control-flow-case id="__35">
        <to-activity-id id="__36">EmplView</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <control-flow-rule id="__37">
      <from-activity-id id="__38">EmplView</from-activity-id>
      <control-flow-case id="__40">
        <from-outcome id="__41">Department</from-outcome>
        <to-activity-id id="__39">executeDeptVOUsingLocation</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

That's it.

JDev Release 11.1.1.4

Region Interation Example 2 - Using ADF Drag And Drop Framework

Problem:
Imagine you have 2 regions, Department and Employee region and want to refresh/requery Employee region after dragging a Department row from the Department region to the Employee region.
This is where ADF's Drag and Drop Framework comes into picture.
The following images shows what I wanted to accomplish:




Constraint:
Emp and Dept tables are not related to each other so the data model is independent of one another.

Solution:

  1. Created two fragments, containing Dept and Emp tables.
  2. On the Dept table, dropped the 'Drag Source' tag from the Operations Component Pallette and specify the operation as COPY and descriminator and Dept.
  3. On the Emp table, drop the 'Drop Target' tag from the Operations Component Pallette and specify a bean method as the drop event listener ( code snippet is shown below ).
  4. For the 'Drop Target' tag, do specify java.lang.Object as the Flavor Class for the child dataFlavor tag, otherwise it will not work.
  5. Include them in their respectative task flows and drop them as regions on the final page.
Here is the code snippet of Department.jsff

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:table value="#{bindings.DeptVO1.collectionModel}" var="row"
            rows="#{bindings.DeptVO1.rangeSize}"
            emptyText="#{bindings.DeptVO1.viewable ? 'No data to display.' : 'Access Denied.'}"
            fetchSize="#{bindings.DeptVO1.rangeSize}" rowBandingInterval="0"
            selectedRowKeys="#{bindings.DeptVO1.collectionModel.selectedRow}"
            selectionListener="#{bindings.DeptVO1.collectionModel.makeCurrent}"
            rowSelection="single"
            binding="#{backingBeanScope.backing_Department.t1}" id="t1">
    <af:column sortProperty="DeptId" sortable="false"
               headerText="#{bindings.DeptVO1.hints.DeptId.label}" id="c2">
      <af:outputText value="#{row.DeptId}" id="ot2">
        <af:convertNumber groupingUsed="false"
                          pattern="#{bindings.DeptVO1.hints.DeptId.format}"/>
      </af:outputText>
    </af:column>
    <af:column sortProperty="DeptName" sortable="false"
               headerText="#{bindings.DeptVO1.hints.DeptName.label}" id="c1">
      <af:outputText value="#{row.DeptName}" id="ot1"/>
    </af:column>
    <af:dragSource actions="COPY" defaultAction="COPY" discriminant="Dept"/>
  </af:table>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_Department-->
</jsp:root>

And here is the code snippet of Employee.jsff

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <af:table value="#{bindings.EmpVO1.collectionModel}" var="row"
            rows="#{bindings.EmpVO1.rangeSize}"
            emptyText="#{bindings.EmpVO1.viewable ? 'No data to display.' : 'Access Denied.'}"
            fetchSize="#{bindings.EmpVO1.rangeSize}" rowBandingInterval="0"
            selectedRowKeys="#{bindings.EmpVO1.collectionModel.selectedRow}"
            selectionListener="#{bindings.EmpVO1.collectionModel.makeCurrent}"
            rowSelection="single"
            binding="#{backingBeanScope.backing_Employee.t2}" id="t2">
    <af:column sortProperty="EmpId" sortable="false"
               headerText="#{bindings.EmpVO1.hints.EmpId.label}" id="c3">
      <af:outputText value="#{row.EmpId}" id="ot3">
        <af:convertNumber groupingUsed="false"
                          pattern="#{bindings.EmpVO1.hints.EmpId.format}"/>
      </af:outputText>
    </af:column>
    <af:column sortProperty="EmpName" sortable="false"
               headerText="#{bindings.EmpVO1.hints.EmpName.label}" id="c2">
      <af:outputText value="#{row.EmpName}" id="ot2"/>
    </af:column>
    <af:column sortProperty="DeptId" sortable="false"
               headerText="#{bindings.EmpVO1.hints.DeptId.label}" id="c1">
      <af:outputText value="#{row.DeptId}" id="ot1">
        <af:convertNumber groupingUsed="false"
                          pattern="#{bindings.EmpVO1.hints.DeptId.format}"/>
      </af:outputText>
    </af:column>
    <af:dropTarget dropListener="#{backingBeanScope.backing_Employee.handleDrop}"
                   actions="COPY">
      <af:dataFlavor flavorClass="java.lang.Object"/>
    </af:dropTarget>
  </af:table>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_Employee-->
</jsp:root>

And here is the code for the Managed Bean that contains the handleDrop drop listener method:

package view.backing;
import java.util.Iterator;
import java.util.List;
import javax.faces.component.UIComponent;

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCIteratorBinding;
import oracle.adf.view.rich.component.rich.data.RichTable;
import oracle.adf.view.rich.datatransfer.DataFlavor;
import oracle.adf.view.rich.datatransfer.Transferable;
import oracle.adf.view.rich.dnd.DnDAction;
import oracle.adf.view.rich.event.DropEvent;

import oracle.binding.BindingContainer;
import oracle.jbo.Row;
import oracle.jbo.ViewObject;
import oracle.jbo.uicli.binding.JUCtrlHierBinding;
import oracle.jbo.uicli.binding.JUCtrlHierNodeBinding;
import org.apache.myfaces.trinidad.model.CollectionModel;
import org.apache.myfaces.trinidad.model.RowKeySet;
public class Employee {
    private RichTable t2;
    public void setT2(RichTable t1) {
        this.t2 = t1;
    }
    public RichTable getT2() {
        return t2;
    }
   
  public DnDAction handleDrop(DropEvent dropEvent){
    Transferable transferable = dropEvent.getTransferable();
    DataFlavor<RowKeySet> rowKeySetFlavor = DataFlavor.getDataFlavor(RowKeySet.class, "Dept");
    RowKeySet rowKeySet = transferable.getData(rowKeySetFlavor);
    if (rowKeySet != null){
      CollectionModel dragModel = null;
      dragModel = transferable.getData(CollectionModel.class);
      if (dragModel!=null) {
        JUCtrlHierBinding binding = null;
        binding = (JUCtrlHierBinding) dragModel.getWrappedData();
        Iterator rowKeySetIterator = rowKeySet.iterator();
        if (binding != null) {
          if (rowKeySetIterator.hasNext()) {
            List rowKey = (List) rowKeySetIterator.next();
            JUCtrlHierNodeBinding nodeBinding = null;
            nodeBinding = binding.findNodeByKeyPath(rowKey);
            Row rw = nodeBinding.getRow();
            queryEmployees( ((oracle.jbo.domain.Number)rw.getAttribute("DeptId")).toString(), dropEvent.getDropComponent());
          }
          return DnDAction.MOVE;
        }
      }
    }
    return DnDAction.MOVE;
  }
  private void queryEmployees(String departmentId, UIComponent dropComponent){
    BindingContext bctx = BindingContext.getCurrent();
    BindingContainer bindings = bctx.getCurrentBindingsEntry();
    DCIteratorBinding employeesIter = (DCIteratorBinding)bindings.get("EmpVO1Iterator");
    ViewObject vo = employeesIter.getViewObject();
    vo.setWhereClause("dept_id = :TheDeptId");
    vo.defineNamedWhereClauseParam("TheDeptId", null, null);
    vo.setNamedWhereClauseParam("TheDeptId", departmentId);
    vo.executeQuery();
  }   
}

Thats it.

JDev Release 11.1.1.4

Saturday, March 19, 2011

Region Interaction Example 1


Imagine you have a table (based on Department) and a region containing a table ( Based on Employees as shown in the image below ), and you want to sync up Employee Region based on the row selected on Departments 1.


To avoid empty space, added the following for each of the table:
1. Set the content delivery as immediate.
2. Set the AutoHeightRows to VO's rangeSize attribute, eg. #{bindings.DeptVO1.rangeSize}.

Constraint:
1. There is no relation/connection between the data model of the first and the second region. EMP and DEPT table are totally independent as shown by the following SQLs:

CREATE TABLE DEPT (
  dept_id   NUMBER(10,0) PRIMARY KEY,
  dept_name VARCHAR2(50) NULL
);

INSERT INTO DEPT VALUES ( 1, 'Finance');
INSERT INTO DEPT VALUES ( 2, 'Sales'); 
INSERT INTO DEPT VALUES ( 3, 'Human Resource'); 
INSERT INTO DEPT VALUES ( 4, 'BPO'); 
INSERT INTO DEPT VALUES ( 5, 'SCM');

CREATE TABLE EMP (
  emp_id   NUMBER(10,0) PRIMARY KEY,
  emp_name VARCHAR2(50) NULL,
  dept_id  NUMBER(10,0) NULL
);

INSERT INTO EMP VALUES ( 1, 'Emp 1', 1);
INSERT INTO EMP VALUES ( 2, 'Emp 2', 2);
INSERT INTO EMP VALUES ( 3, 'Emp 3', 3);
INSERT INTO EMP VALUES ( 4, 'Emp 4', 4);
INSERT INTO EMP VALUES ( 5, 'Emp 5', 5);
INSERT INTO EMP VALUES ( 6, 'Emp 6', 1);
INSERT INTO EMP VALUES ( 7, 'Emp 7', 2);
INSERT INTO EMP VALUES ( 8, 'Emp 8', 3);
INSERT INTO EMP VALUES ( 9, 'Emp 9', 4);
INSERT INTO EMP VALUES ( 10, 'Emp 10', 5);

2. The region ( or the task flow ) takes an input parameter as Department Id.
3. The task flow first have method activity as a default activity that executes the EmpVO based on the department ID provided. It then transfers the control to the view activity that contains the employees table.

Problem:
You want to sync up Employee Region based on the row selected on Departments table, as shown in below images:


And



Solution:
1. Expose the Department ID of the Department table as an attribute binding.
2. Pass the 1 attribute binding input value as the parameter to the task flow binding, eg #{bindings.DeptId.inputValue}.
3. Set the refresh property of the task flow binding as 'ifNeeded'.
4. Set the partial trigger of second region to refer table 1

Here is the code snippet for the page ( that contains the table and task flow region):
<?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" binding="#{backingBeanScope.backing_untitled2.d1}">
      <af:messages binding="#{backingBeanScope.backing_untitled2.m1}" id="m1"/>
      <af:form id="f1" binding="#{backingBeanScope.backing_untitled2.f1}">
        <af:table value="#{bindings.DeptVO1.collectionModel}" var="row"
                  rows="#{bindings.DeptVO1.rangeSize}"
                  emptyText="#{bindings.DeptVO1.viewable ? 'No data to display.' : 'Access Denied.'}"
                  fetchSize="#{bindings.DeptVO1.rangeSize}"
                  rowBandingInterval="0"
                  selectedRowKeys="#{bindings.DeptVO1.collectionModel.selectedRow}"
                  selectionListener="#{bindings.DeptVO1.collectionModel.makeCurrent}"
                  rowSelection="single"
                  binding="#{backingBeanScope.backing_untitled2.t1}" id="t1"
                  contentDelivery="immediate"
                  autoHeightRows="#{bindings.DeptVO1.rangeSize}"
                  columnStretching="last">
          <af:column sortProperty="DeptId" sortable="false"
                     headerText="#{bindings.DeptVO1.hints.DeptId.label}"
                     id="c1">
            <af:outputText value="#{row.DeptId}" id="ot1">
              <af:convertNumber groupingUsed="false"
                                pattern="#{bindings.DeptVO1.hints.DeptId.format}"/>
            </af:outputText>
          </af:column>
          <af:column sortProperty="DeptName" sortable="false"
                     headerText="#{bindings.DeptVO1.hints.DeptName.label}"
                     id="c2">
            <af:outputText value="#{row.DeptName}" id="ot2"/>
          </af:column>
        </af:table>
        <af:spacer width="10" height="10"
                   binding="#{backingBeanScope.backing_untitled2.s1}" id="s1"/>
        <af:region value="#{bindings.EmpVOExecutionTaskFlow1.regionModel}"
                   id="r1" binding="#{backingBeanScope.backing_untitled2.r1}"
                   partialTriggers="::t1"/>
      </af:form>
    </af:document>
  </f:view>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_untitled2-->
</jsp:root>

And here comes its page definition file:

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                version="11.1.1.59.23" id="untitled1PageDef"
                Package="view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables"/>
    <iterator Binds="DeptVO1" RangeSize="25" DataControl="AppModuleDataControl"
              id="DeptVO1Iterator"/>
    <taskFlow id="EmpVOExecutionTaskFlow1"
              taskFlowId="/WEB-INF/EmpVOExecutionTaskFlow.xml#EmpVOExecutionTaskFlow"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding"
              Refresh="ifNeeded">
      <parameters>
        <parameter id="deptId" value="#{bindings.DeptId.inputValue}"/>
      </parameters>
    </taskFlow>
  </executables>
  <bindings>
    <tree IterBinding="DeptVO1Iterator" id="DeptVO1">
      <nodeDefinition DefName="model.DeptVO" Name="DeptVO10">
        <AttrNames>
          <Item Value="DeptId"/>
          <Item Value="DeptName"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
    <attributeValues IterBinding="DeptVO1Iterator" id="DeptId">
      <AttrNames>
        <Item Value="DeptId"/>
      </AttrNames>
    </attributeValues>

  </bindings>
</pageDefinition>

Here is the task flow (embedded as a region specified earlier ) code snippet:

<?xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
  <task-flow-definition id="EmpVOExecutionTaskFlow">
    <default-activity id="__4">executeEmpVO</default-activity>
    <input-parameter-definition id="__2">
      <name id="__1">deptId</name>
      <value>#{pageFlowScope.deptIdDef}</value>
      <class>oracle.jbo.domain.Number</class>
      <required/>
    </input-parameter-definition>
    <managed-bean id="__8">
      <managed-bean-name id="__5">backing_EmpView</managed-bean-name>
      <managed-bean-class id="__7">view.backing.EmpView</managed-bean-class>
      <managed-bean-scope id="__6">backingBean</managed-bean-scope>
      <!--oracle-jdev-comment:managed-bean-jsp-link:1EmpView.jsff-->
    </managed-bean>
    <view id="EmpView.jsff">
      <page>/EmpView.jsff</page>
    </view>
    <method-call id="executeEmpVO">
      <method>#{bindings.executeEmpVO.execute}</method>
      <outcome id="__3">
        <fixed-outcome>executeEmpVO</fixed-outcome>
      </outcome>
    </method-call>
    <control-flow-rule id="__9">
      <from-activity-id id="__10">executeEmpVO</from-activity-id>
      <control-flow-case id="__13">
        <to-activity-id id="__12">EmpView.jsff</to-activity-id>
      </control-flow-case>
    </control-flow-rule>
    <use-page-fragments/>
  </task-flow-definition>
</adfc-config>

And here is the code of the Application Module that exposes executeEmpVO as a Client Interface method.

package model;
import model.common.AppModule;
import oracle.jbo.ViewObject;
import oracle.jbo.server.ApplicationModuleImpl;
import oracle.jbo.server.ViewObjectImpl;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Sat Mar 19 20:43:03 IST 2011
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------
public class AppModuleImpl extends ApplicationModuleImpl implements AppModule {
    /**
     * This is the default constructor (do not remove).
     */
    public AppModuleImpl() {
    }
    /**
     * Container's getter for EmpVO1.
     * @return EmpVO1
     */
    public ViewObjectImpl getEmpVO1() {
        return (ViewObjectImpl)findViewObject("EmpVO1");
    }
    /**
     * Container's getter for DeptVO1.
     * @return DeptVO1
     */
    public ViewObjectImpl getDeptVO1() {
        return (ViewObjectImpl)findViewObject("DeptVO1");
    }
   
    public void executeEmpVO(oracle.jbo.domain.Number deptId){
      ViewObject vo = this.getEmpVO1();
      // Set the two design time named bind variables
      vo.setWhereClause("dept_id = :TheDeptId");
      vo.defineNamedWhereClauseParam("TheDeptId", null, null);
      vo.setNamedWhereClauseParam("TheDeptId", deptId);
      vo.executeQuery();
    }

}

Thats it.

JDev Release 11.1.1.4

Friday, March 18, 2011

How ADF's 'Select Many Shuttle' Component Works

I was trying to find out how Select Many Shuttle (SMS) component works and was finding almost similar posts which were using iterator binding. What I wanted is how just this component works without any binding details, and with some managed bean methods. So if you are also looking for the same, this blog can help.

I wanted to create a small page with SMS component in it and a command button. On clicking the command button, I wanted to print the selected or moved items content. The following image shows what I wanted to do:



To achieve this, I created the following ShuttlePage.jspx page:

<?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" binding="#{backingBeanScope.BackingShuttlePage.d1}">
      <af:form id="f1" binding="#{backingBeanScope.BackingShuttlePage.f1}">
        <af:selectManyShuttle label="Employee Shuttle"
                              binding="#{backingBeanScope.BackingShuttlePage.sms1}"
                              id="sms1"
                              value="#{backingBeanScope.BackingShuttlePage.selectedEmployees}">
          <f:selectItems
value="#{backingBeanScope.BackingShuttlePage.allEmployees}"
                         binding="#{backingBeanScope.BackingShuttlePage.si1}"
                         id="si1"/>
        </af:selectManyShuttle>
        <af:commandButton text="Print Selection"
                          binding="#{backingBeanScope.BackingShuttlePage.cb1}"
                          id="cb1"
                          actionListener="#{backingBeanScope.BackingShuttlePage.printSelection}"/>
      </af:form>
    </af:document>
  </f:view>
  <!--oracle-jdev-comment:auto-binding-backing-bean-name:BackingShuttlePage-->
</jsp:root>


The above page is making use of the following backing bean:

package view.backing;
import oracle.adf.view.rich.component.rich.RichDocument;
import oracle.adf.view.rich.component.rich.RichForm;
import java.util.ArrayList;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCIteratorBinding;
import oracle.adf.view.rich.component.rich.input.RichSelectManyShuttle;
import oracle.adf.view.rich.component.rich.nav.RichCommandButton;

public class BackingShuttlePage {
    private RichForm f1;
    private RichDocument d1;

    List allEmployees;
    List selectedEmployees;

    private RichSelectManyShuttle sms1;
    private UISelectItems si1;
    private RichCommandButton cb1;

    public void setF1(RichForm f1) {
        this.f1 = f1;
    }

    public RichForm getF1() {
        return f1;
    }

    public void setD1(RichDocument d1) {
        this.d1 = d1;
    }

    public RichDocument getD1() {
        return d1;
    }

    public List getAllEmployees() {
      if (allEmployees == null) {
        allEmployees = new ArrayList<javax.faces.model.SelectItem>();
        allEmployees.add(new javax.faces.model.SelectItem("Employee 1"));
        allEmployees.add(new javax.faces.model.SelectItem("Employee 2"));
        allEmployees.add(new javax.faces.model.SelectItem("Employee 3"));
        allEmployees.add(new javax.faces.model.SelectItem("Employee 4"));
        allEmployees.add(new javax.faces.model.SelectItem("Employee 5"));
        allEmployees.add(new javax.faces.model.SelectItem("Employee 6"));
      }
      return allEmployees;
    }
   
    public List getSelectedEmployees() {
      if (selectedEmployees == null) {
        selectedEmployees = new ArrayList<javax.faces.model.SelectItem>();
      }
      return selectedEmployees;   
    }
   
   
public void setSelectedEmployees(List selectedItems) {
        this.selectedEmployees = selectedItems;
    }
   
    public void printSelection(ActionEvent actionEvent) {
        List l = this.getSelectedEmployees();
        StringBuilder text = new StringBuilder("Size = ").append(getSelectedEmployees().size()).append(", Items added are: ");
        for (int i = 0; i <l.size(); i++ ) {
            text.append("Item ").append(i).append(" = ").append(l.get(i)).append(", ");
        }  
        FacesContext fctx = FacesContext.getCurrentInstance();
        FacesMessage message = new FacesMessage(text.toString());
        fctx.addMessage(null, message);
    }

    public void setSms1(RichSelectManyShuttle sms1) {
        this.sms1 = sms1;
    }

    public RichSelectManyShuttle getSms1() {
        return sms1;
    }

    public void setSi1(UISelectItems si1) {
        this.si1 = si1;
    }

    public UISelectItems getSi1() {
        return si1;
    }


    public void setCb1(RichCommandButton cb1) {
        this.cb1 = cb1;
    }

    public RichCommandButton getCb1() {
        return cb1;
    }
}


Important points are highlighted above. The things to note are the value bindings for selectManyShuttle and selectItems tags. Remember that the value binding for selectManyShuttle represents the selected values (right shuttle values) and the value binding for selectItems represents non-selected values (left shuttle values). Also provided the method for setting selected employees, setSelectedEmployees, as without it also the example does not work and this method is called as soon as we click the Print Selection command button, as shown in the following figure:


So, the selected values are automatically added in the list by the component. Thats what I wanted to know whether the component modifies the list with the selected values. Later on we can iterate over those values to do whatever our logic demands, like printing them in this example.
Hope this blog will save some of your time.

JDev Release 11.1.1.4