Thursday, November 25, 2010

ADF Model: Executing view accessor programatically

In this post, I'll show how to execute a VO(or it's view criteria) added as a view accessor in another VO.

Sample Use case:
For example we have two VOs DeptVO(based on only DeptEO) and EmpDeptVO(based on empEO and DeptEO) and the DeptVO is added a view accessor in EmpDeptVO and we need to programmatically execute this view accessor to get the Deptno for the passed Dname (assuming it DeptVO has a view criteria that takes Dname as a parameter or bind variable) and set the DetpNo for the newly created row in EmpDeptVO. You can download the sample application from here.

Implementation Steps:
1. Create DeptVO based on DeptEO and create a view criteria "findByDeptName" that queries based on the bind variable 'Bind_Dname'.

2. Create EmpDeptVO and add the DeptVO as view accessor (DeptVA) and select the view criteria "findByDeptName" in the VA definition.

3. Generate RowImpl class for EmpDeptVO. The generated class name will be EmpDeptVORowImpl.

4. Now, write a method say "getDeptIdFromViewAccessor" in EmpDeptVORowImpl that takes 'Dname' as parameter that executes the view accessor DeptVA and returns the DeptId for the passed 'Dname'. Code below:
public Object getDeptIdFromViewAccessor(String deptName) { //here getDeptVA is the getter for the view accessor 'DeptVA' in EmpDeptVO RowSet rowSet = this.getDeptVA(); //setting the range size to -1 to get all the rows rowSet.setRangeSize(-1); //passing the value for bind parameter for the view accessor rowSet.setNamedWhereClauseParam("Bind_Dname", deptName); //executing the view accessor rowSet.executeQuery(); //storing the first row in the row set in deptRow (there can be multiple rows in the row set based on the criteria) Row deptRow = rowSet.first(); //if the dpetRow is not null, returning the Deptno return (deptRow != null) ? deptRow.getAttribute("Deptno") : null; }


5. Call the above EmpDeptVORowImpl method in AmImpl's method say "testExecuteViewAccessor" passing the Dname for which the DeptId is required. Now, set this deptId to the newly created EmpDeptVO row in AMImpl method. Code below:
public void testExecuteViewAccessor(){ ViewObjectImpl empDeptVO = this.getEmpDeptVO(); Row row = empDeptVO.createRow(); empDeptVO.insertRow(row); EmpDeptVORowImpl empDeptRow = (EmpDeptVORowImpl)empDeptVO.getCurrentRow(); //calling the EmpDeptVORowImpl method to get the deptId for the deptName 'accounting' Object deptId = empDeptRow.getDeptIdFromViewAccessor("accounting"); System.out.println("DeptId from view accessor: "+deptId); //setting the deptId to the current row's 'Deptno' attribute. empDeptRow.setDeptno((Number)deptId); }


Here is the sample output on running this method using AM tester.

That's it. Now you know how to execute the view accessor(VA) programatically, how to pass the parameters for the view criteria selected in VA definition, how to get and use the results in AMImpl methods.

PS: DeptId and Deptno are used interchangeably in this post.

Thursday, November 11, 2010

ADF UI - selectionListener example for single-select af:table

There are situations when we require to perform some action on selecting a row in a single-select table. For such cases, we need to remove the default selectionListener and specify our own selectionListener method from the backing bean and write our logic within the method.

The generated default selectionListener for the table would be like selectionListener="#{bindings.EmpDeptVO.collectionModel.makeCurrent}". This will actually make the selected row as current row. As we're removing the default selectionListener, we need to call the same default selection listener method in the custom selectionListener method. Then only we can get handle to the selected row.

Sample Use Case:
Suppose I have employees table and when I select a row in the table, immediately I need to show a popup with the selected employee name. Sample application can be downloaded from here.

Implementation Steps:
1. Drop the VO as a table in the jsf page. Remove the default selectionListener and specify your own backing bean method for the selectionListener.
<af:table value="#{bindings.EmpDeptVO.collectionModel}" var="row" rows="#{bindings.EmpDeptVO.rangeSize}" emptyText="#{bindings.EmpDeptVO.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.EmpDeptVO.rangeSize}" rowBandingInterval="0" rowSelection="single" id="t1" partialTriggers=":::qryId1 ::ctb1 ::commandToolbarButton1" columnStretching="column:c1" styleClass="AFStretchWidth" columnSelection="multiple" first="0" contentDelivery="immediate" autoHeightRows="10" binding="#{pageFlowScope.ExampleBean.searchResultsTable}" selectionListener="#{pageFlowScope.ExampleBean.rowSelected}">

2. In the custom selectionListener method, call the default selectionListener method which will set the selected row as the current row. We can use ADFUtil.invokeEL() to invoke the same default EL expression method and the selectionEvent as the parameter. Sample code below.
ADFUtil.invokeEL("#{bindings.EmpDeptVO.collectionModel.makeCurrent}", new Class[] { SelectionEvent.class }, new Object[] { selectionEvent });

3. Now, the selected row is set as current row and you can get the current row reference by using the below EL. Now, you can get all attributes of the selected row by using the above reference.
Row selectedRow = (Row)ADFUtil.evaluateEL("#{bindings.EmpDeptVOIterator.currentRow}");

4. Get the attribute value of Ename from the selected row and set it to a pageFlowScope variable say #{pageFlowScope.empName}. Add a popup in the jsf page that displays the #{pageFlowScope.empName} as the selected employee. Write your logic to invoke the popup in the same selectionListener method.

Popup code in the jsf page:
<af:popup id="p1" binding="#{pageFlowScope.ExampleBean.displayNamePopup}"> <af:dialog id="d1" type="ok" title="Alert"> <af:outputText value="You have selected Employee: #{pageFlowScope.empName}" id="ot4"/> </af:dialog> </af:popup>

Backing bean containing the selectionListener method and popup binding:
public class ExampleBean { private RichPopup displayNamePopup; public void rowSelected(SelectionEvent selectionEvent) { ADFUtil.invokeEL("#{bindings.EmpDeptVO.collectionModel.makeCurrent}", new Class[] { SelectionEvent.class }, new Object[] { selectionEvent }); Row selectedRow = (Row)ADFUtil.evaluateEL("#{bindings.EmpDeptVOIterator.currentRow}"); ADFUtil.setEL("#{pageFlowScope.empName}", selectedRow.getAttribute("Ename")); PopupUtil.invokePopup(displayNamePopup.getClientId(FacesContext.getCurrentInstance())); ; } public void setDisplayNamePopup(RichPopup displayNamePopup) { this.displayNamePopup = displayNamePopup; } public RichPopup getDisplayNamePopup() { return displayNamePopup; } }

That's it. When the row is selected, the selectionListener method in the bean is called, current row is set to selected row, stores the current row's Ename attribute to pageFlowScope variable 'empName', invoke the popup programatically, and the same pageFlowScope variable will be displayed in the popup as selected employee name.

Sample screen shots:

Wednesday, November 10, 2010

ADF UI - Creating dropdown or menu buttons

Creating dropdown or menu buttons using ADF is easy. A dropdown menu button is the button with a dropdown icon which will display list of menu items on clicking the button or dropdown icon. Just go through the below steps to create a menu button.

1. Add a af:commandButton or af:toolbarButton where you need to dropdown button. Now, in the 'popup' facet, drag and drop af:menu and add af:menu item(i.e., af:commandMenuItem)s to the af:menu. Sample code below:
<af:commandToolbarButton text="Options" id="commandToolbarButton1" disabled="#{bindings.EmpDeptVO.currentRow==null}" partialTriggers="t1" actionDelivery="none"> <f:facet name="popup"> <af:menu id="m1"> <af:commandMenuItem text="View" id="cmi1" action="view"/> <af:commandMenuItem text="Edit" id="cmi2" action="edit"/> <af:commandMenuItem text="Delete" id="cmi3" actionListener="#{bindings.Delete.execute}" partialTriggers="t1"/> </af:menu> </f:facet> </af:commandToolbarButton>

The above code in Structure window will look like this:

2. The above code will render the menu button as shown below:

3. There are two types of menu buttons:
i. Which will display dropdown list or menu items on clicking the 'dropdown' icon. This button will have a separator (vertical line '|') between the button text and the dropdown icon. It'll look like below. This is actually the default behavior. You can specify action and actionListener for this button and on clicking the button it'll cause navigation or fire actionListener based on the specified action and actionListener respectively.


ii. Which will display drowdown list on clicking the button itself. This button won't have a separator (vertical line '|') between the button text and the dropdown icon. It'll look like below. To get this behavior, you need to set actionDelivery="none" for the button. If you set this property, you can't specify either action or actionListener for the button as it won't fire either action or actionListener.

That's it. You can specify action and actionListener for the menu items in the dropdown menu and do whatever you want.

Sunday, November 7, 2010

ADF UI - Getting all pagedef attributes of a particular VO iterator programatically (find transients among them as well :) )

This might be rare requirement to get handle on pagedef attributes of a particular VO iterator. But, it would be quite useful for some scenarios like when you want to get the values of attributes of a particular iterator present in the current jsff page (not each and every VO attribute) and compare them with the original values to know if the user has changed the attribute through the UI. It would be waste of effort to check all VO attributes if they're changed as the user can only change the attributes present in the UI jsff pages.

But, it's not as easy as getting a reference to bindings (DCBindingContainer) and call the method getAttributeBindngs() on it. Even though it returns all the pagedef attribute bindings, they'll include attributes of all VO iterators in the pagedef. The tricky part is to get attributes of only a particular VO iterator in the pagedef. Here, in this post, I'll explain how to do that.

I did some research and wrote the a method to retrieve all the pagedef attributes of the VO iterator specified by 'VOName' as a parameter. This method also has the logic to find out the transient attributes present the VO. This would be quite useful when you want to find out the transient attributes of a VOIterator in the pagedef and to do logic on them (like skipping them for comparision, etc.,).

Method code in the bean:
public void storePageDefAttrs(String VOName) { DCBindingContainer binding = (DCBindingContainer)ADFUtil.evaluateEL("#{bindings}"); //getting the attribute bindings in the pagedef, it'll contain all pagedef attribute bindings of all VO iterators in pagedef List attrBindings = binding.getAttributeBindings(); //getting iterator for attribute bindings Iterator itr = attrBindings.iterator(); //getting the current row for the VOName ViewRowImpl row = (ViewRowImpl)ADFUtil.evaluateEL("#{bindings." + VOName + "Iterator.currentRow}"); //pageDefAttrs will store pageDef attribute names for the given VO specified by 'VOName' List pageDefAttrs = new ArrayList(); //transientAttrs will store trasient attribute names added in the pagedef for the given VO specified by 'VOName' List transientAttrs = new ArrayList(); //iterating through all attribute bindings while (itr.hasNext()) { AttributeBinding attrBinding = (AttributeBinding)itr.next(); //getting the attribute name String attrName = attrBinding.getName(); //getting the iterator binding for the attribute DCIteratorBinding attrIterBinding = (DCIteratorBinding)ADFUtil.evaluateEL("#{bindings." + attrName + ".iteratorBinding}"); //getting the iterator name for the iterator binding String attrIterBidningName = attrIterBinding.getVOName(); //checking if this attribute's iterator name is equal to the passed VOName if (VOName.equals(attrIterBidningName)) { //if the attributes' iterator name is equal to passed VOName, this attributes belongs to the VO specified by 'VOName'. So adding it to pageDefAttrs. pageDefAttrs.add(attrName); //getting the attribute index int attrIndex = row.getAttributeIndexOf(attrName); //getting the view attribute definition ViewAttributeDefImpl vDef = (ViewAttributeDefImpl)row.getViewDef().getAttributeDef(attrIndex); //checking if it's a transient attribute. boolean transientPageDefAttr = (vDef.getEntityAttributeDef() == null) || (vDef.getEntityAttributeDef().getColumnName() == null); //if it's transient, adding it to transientAttrs list if (transientPageDefAttr) { transientAttrs.add(attrName); } } } //stroing the pagedef attribute names and transient attribute names for the given VOName in the pageFlowScope variables. ADFUtil.setEL("#{pageFlowScope.pageDefAttrs}", pageDefAttrs); ADFUtil.setEL("#{pageFlowScope.trasientPageDefAttrs}", transientAttrs); }

The above code is self explanatory from the comments inline. It takes 'VOName' as a parameter and finally stores all pagedef attributes(including transients) for that VO in the ArrayList 'pageDefAttrs'. It'll also store transient attributes of the VO in the ArrayList 'transientAttrs'.

I devloped a sample application that prints the all attributes and transient attributes present in the pagedef. When you download and run the application, it'll open a search page. Search for a record and select one row and click on 'Edit' button. Now, you'll goto edit page and when you click on 'Show Pagedef Attributes' button, it'll print all attributes as well as the transient attributes present in the pagedef.

Attributes in EmpDeptVO definition:

Attributes in DeptVO definition:


Pagedef file containing some of the attributes from EmpDeptVO and DeptVO:
<?xml version="1.0" encoding="UTF-8" ?> <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="11.1.1.56.60" id="EditEmployeePageDef" Package="com.demo.fragments"> <parameters/> <executables> <variableIterator id="variables"/> <iterator Binds="EmpDeptVO" RangeSize="25" DataControl="DemoAMDataControl" id="EmpDeptVOIterator"/> <iterator Binds="DeptVO" RangeSize="25" DataControl="DemoAMDataControl" id="DeptVOIterator"/> </executables> <bindings> <attributeValues IterBinding="EmpDeptVOIterator" id="Ename"> <AttrNames> <Item Value="Ename"/> </AttrNames> </attributeValues> <attributeValues IterBinding="EmpDeptVOIterator" id="Job"> <AttrNames> <Item Value="Job"/> </AttrNames> </attributeValues> <attributeValues IterBinding="EmpDeptVOIterator" id="Loc"> <AttrNames> <Item Value="Loc"/> </AttrNames> </attributeValues> <attributeValues IterBinding="EmpDeptVOIterator" id="Mgr"> <AttrNames> <Item Value="Mgr"/> </AttrNames> </attributeValues> <attributeValues IterBinding="DeptVOIterator" id="Dname"> <AttrNames> <Item Value="Dname"/> </AttrNames> </attributeValues> <attributeValues IterBinding="DeptVOIterator" id="Loc1"> <AttrNames> <Item Value="Loc"/> </AttrNames> </attributeValues> <attributeValues IterBinding="EmpDeptVOIterator" id="attribute1"> <AttrNames> <Item Value="attribute1"/> </AttrNames> </attributeValues> <attributeValues IterBinding="EmpDeptVOIterator" id="attribute2"> <AttrNames> <Item Value="attribute2"/> </AttrNames> </attributeValues> </bindings> </pageDefinition>



Ouptput attributes on running the sample application:


So, from the above screen shot you can see our method printed the EmpDeptVO's attributes and transient attributes present in the pagdef correctly.

Enjoy :) !

PS: In case, if you're aware of better approach than the above one, please share with me too. Thank you :)

Saturday, November 6, 2010

ADF UI - Region Interaction: From parent page to child region task flow

If you drop a task flow as a region in a jsf page and if you want to refresh the task flow region based on some action in the jsf, please go through this post.

Sample UseCase:
For example if a Jsf Page contains departments table and on selecting a department from the table and click on button 'Show Employees', it should display the employees in the selected department. But suppose the employee details should come from a different task flow. We need to establish the communication between the parent jsf page and the task flow region in it. Sample application for this example can be downloaded from here.

Implementation Steps:

Task flow refresh Principle:
If you drop a task flow that takes some parameters as a region in a jsff page, the task flow binding will be added in the pagedef file. If you set refresh='ifNeeded' for the task flow binding, the task flow will be re-executed each time when the value for one of the passed parameters got changed. To be clear, here re-execution means the task-flow will re-execute from it's default activity in the task flow diagram.

This example depends on the above principle.

1. Create a jsff page and include departments table and add a button 'Show Employees'.
2. Create a task flow that takes deptId as parameter and fetches the employees present the given department and shows them in a table.

Task flow diagram with input parameter deptId:

3. Specify actionListener for the 'Show Employees' button and in the actionListener method, get the deptId selected in departments table and set to to a pageFlowScope variable.

Java bean containing the actionListener method for 'Show Employees' button:
public class ExampleBean { private Integer deptId; public void buttonClicked(ActionEvent ae){ Row currentDeptRow=(Row)ADFUtil.evaluateEL("#{bindings.DeptVO.currentRow}"); ADFUtil.setEL("#{pageFlowScope.deptId}",currentDeptRow.getAttribute("Deptno")); } public void setDeptId(Integer deptId) { this.deptId = deptId; } public Integer getDeptId() { return deptId; } }


4. Drop the employee details task flow as a region in the jsf page. Pass the same above pageFlowScope variable as a parameter to the dropped taskflow. Specify refresh="ifNeeded" for the task flow binding in pagedef.

Task flow binding in the pagedef:

   <taskFlow id="TaskFlow21"               taskFlowId="/com/demo/taskflows/TaskFlow2.xml#TaskFlow2"               xmlns="http://xmlns.oracle.com/adf/controller/binding"               Refresh="ifNeeded" activation="deferred">       <parameters>         <parameter id="deptId" value="#{pageFlowScope.deptId}"/>       </parameters>     </taskFlow>


That's it. This is how it works: When the user selects department in jsff and clicks on 'Show Employees' button, the actionListener method sets the pageFlowScope variable with the deptId selected and the same will be passed to the dropped task flow. As the task flow re-executes if any of it's input parameter changes, the employees will be queried based on passed deptId and the task flow will refreshed with the corresponding employee details. No 'partialTriggers' is required in this case. Refresh of task flow will be triggered automatically when the input parameter value is changed.

Sample screen shots:

Friday, November 5, 2010

ADF UI - Region Interaction: Between two peer region task flows present in a single parent page

Generally, if we want to refresh a field or part of the page, we use ppr (partial page rendering) in ADF. We just specify the id of the component (whose state change should trigger refresh) for the partialTriggers property of the required component to be refreshed. It's that simple. But, when we use task flows as regions in our pages, many times we need to establish communication between the parent page containing the region and the region itself. Similarly if there are two regions in the same jsff and some times we need to refresh one region based on some actin done on another region. Here, in this post I'm going to explain this case.

Sample UseCase:
For example, we have two task flows- one task flow displays the departments in a table and the second task flow shows the employees present in department for which the Deptno is passed as a task flow parameter. Now, the requirement is that based on the department selected in the first region, the second region needs to be refreshed with the employees in that particular selected department provided these two regions are dropped in a single jsf page. Sample example application can be downloaded from here. Here, actually we're establishing a communication between the two regions present in the same jsf page.

Implementation Details:
Before going through the implmentation steps, lets have a quick review of task flow basics.

If you drop a task flow that takes some parameters as a region in a jsff page, the task flow binding will be added in the pagedef file. If you set refresh='ifNeeded' for the task flow binding, the task flow will be re-executed each time when the value for one of the passed parameters got changed. To be clear, here re-execution means the task-flow will re-execute from it's default activity in the task flow diagram.

In other words, if you drop task flow TF that takes parameters param1,param2 in a jsf page abc.jsff, and if you specify refresh='ifNeeded' for the task flow binding in pagdef, and if you give the values for param1 and param2 as #{pageFlowScope.param1} and #{pageFlowScope.param2}, the task flow will be re-executed if any of the pageflowScope param values changed to a different value.

Knowing the above point is very important to implement interaction between two regions.

Implementation Steps:
1. Make two task flows ready -
i. TaskFlow1 - This task flow displays departments table with a button 'Show Employees'.
ii. TaskFlow2 - This task flow takes a parameter deptId and based on the passed deptId, it'll query the employees in the dept and displays them in a table.

TaskFlow2 Diagram:

2. Create a java bean with the property deptId and generate accessors for it. Now, create data control for the bean.

Java bean containing the property 'deptId':
package com.demo.beans; public class SharedBean { private Integer deptId; public void setDeptId(Integer deptId) { this.deptId = deptId; } public Integer getDeptId() { return deptId; } }

3. Now, in the actionListener method for 'Show Employees' button in task flow1, just get the selected deptId and set to the shared bean data control property.

ActionListener method code:
public void buttonClicked(ActionEvent ae) { Row currentDeptRow = (Row)ADFUtil.evaluateEL("#{bindings.DeptVO.currentRow}"); ADFUtil.setEL("#{bindings.deptId.attributeValue}", currentDeptRow.getAttribute("Deptno")); }

4. Drop the two task flows in a single jsff page. It'll ask for the parameter for the TaskFlow2, give the same shared bean data control property 'deptId' as the parameter. Don't forget to set the 'refresh' property for the TaskFlow2 task flow binding to 'ifNeeded'.

Parent jsff page code containing these two regions:
<?xml version='1.0' encoding='windows-1252'?> <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:f="http://java.sun.com/jsf/core"> <af:panelGroupLayout id="pgl1"> <af:showDetailHeader text="Interaction between two peer regions" disclosed="true" id="sdh1"> <af:panelGroupLayout id="pgl3" layout="scroll"> <af:region value="#{bindings.TaskFlow11.regionModel}" id="r1"/> </af:panelGroupLayout> <af:spacer width="10" height="10" id="s1"/> <af:panelGroupLayout id="pgl4" layout="scroll"> <af:region value="#{bindings.TaskFlow21.regionModel}" id="r2"/> </af:panelGroupLayout> </af:showDetailHeader> </af:panelGroupLayout> </jsp:root>

Parent Jsff pagedef file:
<?xml version="1.0" encoding="UTF-8" ?> <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="11.1.1.56.60" id="ParentPagePageDef" Package="com.demo.fragments"> <parameters/> <executables> <variableIterator id="variables"/> <taskFlow id="TaskFlow11" taskFlowId="/com/demo/taskflows/TaskFlow1.xml#TaskFlow1" activation="deferred" xmlns="http://xmlns.oracle.com/adf/controller/binding" Refresh="ifNeeded"/> <taskFlow id="TaskFlow21" taskFlowId="/com/demo/taskflows/TaskFlow2.xml#TaskFlow2" activation="deferred" xmlns="http://xmlns.oracle.com/adf/controller/binding" Refresh="ifNeeded"> <parameters> <parameter id="deptId" value="#{bindings.deptId.attributeValue}"/> </parameters> </taskFlow> <iterator Binds="root" RangeSize="25" DataControl="SharedBean" id="SharedBeanIterator"/> </executables> <bindings> <attributeValues IterBinding="SharedBeanIterator" id="deptId"> <AttrNames> <Item Value="deptId"/> </AttrNames> </attributeValues> </bindings> </pageDefinition>

That's it. Now, when the user selects a dept in TaskFlow1 region and clicks on 'Show Employees', the second task flow will be re-executed as the actionListener method changes value of the input parameter of the TaskFlow2. This triggers re-execution of TaskFlow2 and corresponding employees in selected dept in TaskFlow1 will be displayed in TaskFlow2.

So, here we're establishing communication by using shared bean data control property. Sample Screenshots below:

ADF UI - How to include Row Header for af:table?

If you're using af:table and row selection is enabled for the table, a must for the table is 'Row Header'. Row header enables the user to select a single row from the table. To add a row header to the table, just add a blank column as the first column and specify width="5", minimumWidth="5" and rowHeader="true" and specify align="right". Don't include any field inside this row header column.

Below is the code for row header. You can copy-paste this code as a first column in the table and that's it. You're done!
<af:column id="c4" rowHeader="true" width="5" minimumWidth="5" align="right"/>

Sample screen shots:
Table without row header:

Table after adding row header:

Thursday, November 4, 2010

ADF UI - Implementing multi-select table (Selecting and processing multiple rows)

When it comes to implementation of multi-select table using Oracle ADF, it'll become little tricky. In this post, I'll explain how easily we can select multiple rows from af:table and process them for whatever need you have.

For example, let's take Employee search screen and from the search results table, let's select multiple employee records and click on 'Delete' button to delete the selected employee records. You can download the sample application from here. Please follow the below mentioned steps:

1. Drag and drop the VO as af:table into the jsff page and from the source remove the properties 'selectedRowKeys' and 'selectionListener'. And set 'rowSelection=multiple'. This will allow us to select multiple rows. You can select the multiple rows by clicking multiple rows pressing 'Ctrl' key or you can do the range selection by pression 'Shift' key.

2. Now, add a binding for the af:table as a bean property. And, write an actionListener method for the 'Delete' button to invoke after selecting multiple rows.

After doing this, the table code would look like this: 
<af:table value="#{bindings.EmpDeptVO.collectionModel}" var="row" rows="#{bindings.EmpDeptVO.rangeSize}" emptyText="#{bindings.EmpDeptVO.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.EmpDeptVO.rangeSize}" rowBandingInterval="0" rowSelection="multiple" id="t1" partialTriggers=":::qryId1 ::ctb1" columnStretching="column:c1" styleClass="AFStretchWidth" columnSelection="multiple" first="0" contentDelivery="immediate" autoHeightRows="10" binding="#{pageFlowScope.ExampleBean.searchResultsTable}">

The bean class having ActionListener method and the table binding:
package com.demo.ui.bean; import com.mpapana.bean.ADFUtil; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.faces.event.ActionEvent; import oracle.adf.model.binding.DCBindingContainer; import oracle.adf.view.rich.component.UIXTable; import oracle.adf.view.rich.component.rich.data.RichTable; import oracle.binding.OperationBinding; public class ExampleBean { private RichTable searchResultsTable; public void setSearchResultsTable(RichTable searchResultsTable) { this.searchResultsTable = searchResultsTable; } public RichTable getSearchResultsTable() { return searchResultsTable; } public void deleteSelectedRows(ActionEvent ae) { //getting table reference from binding UIXTable table = getSearchResultsTable(); //getting iterator to iterate over selected row keys Iterator selection = table.getSelectedRowKeys().iterator(); List rowIndexes = new ArrayList(); while (selection.hasNext()) { Object rowKey = selection.next(); //setting the rowKey to table one by one table.setRowKey(rowKey); int index = table.getRowIndex(); //adding selected row indexes to a List rowIndexes.add(index); System.out.println(".... Row row=" + table.getRowData() + "\t" + index); } DCBindingContainer bindings = (DCBindingContainer)ADFUtil.evaluateEL("#{bindings}"); //getting operation binding reference to the AMImplMethod deleteSelectedRows() OperationBinding opBinding = bindings.getOperationBinding("deleteSelectedRows"); //passing the selected row keys list to the AMImpl method opBinding.getParamsMap().put("selectedRowIndexes", rowIndexes); opBinding.execute(); } }

In the above bean method deleteSelectedRows, we're getting the selected row keys and iterating over them and setting the row key to the table. We're doing this because, we hae to store te the table's row index in a list and this list will passed to the AMImpl method where the actual processing the rows is done.

4. Write a method in AMImpl (deleteSelectedRows) which takes the List selectedRowIndexes as an argument. This method iterates over the selected row indeces and gets the row at the selected index and performs either update or delete on that row (which represents the selected row in the UI table).
public void deleteSelectedRows(List selectedRowIndexes) { ViewObjectImpl empDeptVO = this.getEmpDeptVO(); for (int i = 0; i < selectedRowIndexes.size(); i++) { //getting the selected row index one by one int selectedRowIndex =(Integer) selectedRowIndexes.get(i); //setting the current row to the row at selected index empDeptVO.setCurrentRowAtRangeIndex(selectedRowIndex); //deleting the current row empDeptVO.removeCurrentRow(); } //committing the transaction this.getTransaction().commit(); }

Suppose, if you need to update the rows, sample code below:
public void updateSelectedRows(List selectedRowIndexes) { ViewObjectImpl empDeptVO = this.getEmpDeptVO(); for (int i = 0; i < selectedRowIndexes.size(); i++) { //getting the selected row index one by one int selectedRowIndex =(Integer) selectedRowIndexes.get(i); //setting the current row to the row at selected index empDeptVO.setCurrentRowAtRangeIndex(selectedRowIndex); Row row = empDeptVO.getCurrentRow(); //updating the Ename attribute row.setAttribute("Ename", row.getAttribute("Ename") + "+"); } //committing the transaction this.getTransaction().commit(); }


5. The same AMImpl method will be invoked from the bean method (refer to: how to invoke AMImpl method in bean) the selected rows will be deleted and the transaction is committed in the AMImpl's method.

That's it. In a nut shell, the bean method will store selected row indexes in a list and the AMImpl method takes that list and does the operations (update or delete) on the rows at those selected indexes.

Sample screen shots:
1. Select two records 'SCOTT' and 'JAMES' and click 'Delete'.
 2. Search again. The two records got deleted and won't appear in search results.

Enjoy :) !
Related Posts with Thumbnails