Sunday, October 31, 2010

ADF UI - Resetting or clearing form fields in ADF popup on clicking 'Cancel'

Sample UseCase: Suppose the popup has input form fields to enter data with say OK and Cancel buttons, and when the user enters data and clicks on 'Cancel' button, we need to close the popup and reset all values entered in the popup's fields. If you don't reset the values, if you invoke the same popup again, it'll show the same previously entered values in the form which is unwanted. So, how to show the popup without the previous values?

Solution:
1. Set popup's 'contentDelivery' property to 'lazyUncached'. Setting this property won't cache the data entered and won't restore the previously entered values. This is highly recommended to set contentDelivery="lazyUncached" for better performance as the popup will be loaded only on invocation but not at the time of page loading.
<af:popup id="popup1" binding="#{pageFlowScope.ClassSectionCreateBean.cancelWarningPopup}" contentDelivery="lazyUncached">


2. If the step1 doesn't work, drop af:resetActionListener as a sub-element to 'Cancel' button in the popup. ResetActionListener will reset the form fields in the popup. We can also call ResetActionListener programmatically in an actionlistener method as follows using Oracle's internal API.
public void saveSections(ActionEvent ae) { //Your logic here ResetActionListener ral = new ResetActionListener(); ral.processAction(ae); }


But, using internal API in the code is not encouraged. There is public API to achieve the same using the below code.
UIComponent comp = actionEvent.getComponent(); oracle.adf.view.rich.util.ResetUtils.reset(comp);


But, this Public API is not yet available in the latest publicly available Jdeveloper available and it'll be available in the upcoming versions. Until then, we need to go ahead using internal API only as shown in step2.

ADF UI - How the default focus is set to popup dialog buttons?

When we use popup dialogs, we want to set default focus on one of the dialog's buttons as we want to navigate or execute some logic on pressing 'Enter' without loosing focus. But many of us not aware how to do that (at least I until sometime back). First, if you set 'type' (e.g., yesNo, okCancel, ok, cancel, yesNoCancel, etc.)of the af:dialog to specify the buttons you want to display, then you're safe. Because ADF framework will automatically set default focus to either 'OK' or 'Yes' button for yesNo, okCancel, ok, yesNoCancel types and to 'Cancel' button if the type is 'cancel'.

But, if you added your own buttons to the dialog buttonbar by specifying 'type' of the popup as 'none', then please note that you can't set default focus to any of these custom buttons. So, first you need to find why you require a custom button instead of the popup's default buttons by specifying 'type' attribute. You can always override the default 'OK' or 'Yes' button labels by setting 'AffirmativeTextAndAccessKey' and similarly 'No' and 'Cancel' button labels by specifying values for 'NoTextAndAccessKey' and 'CancelTextAndAccessKey' attributes. If your requirement is more than a label, like to have multiple actions in popup by specifying multiple buttons, then you need to consider adding additional buttons along with default buttons that comes by specifying the 'type' attribute for the dialog. If you just use the custom buttons only, then the default focus will be on the close(X) icon at the top right corner of the popup. That means, if you press 'Enter', the popup will be closed as the focus is on close icon.

If you want to execute some logic on pressing the default dialog buttons, you need to write the dialog listener. Similarly, if you need to do some processing on pressing the custom button that you added in the dialog buttonbar, you need to write actionlistener for the button. If you use both default buttons as well as custom buttons, you would require both dialoglistener and actionlistener.

Sample dialog listener code for dialog type 'yesNoCancel':
public void sampleDialogListener(DialogEvent dialogEvent) { if ("yes".equals(dialogEvent.getOutcome())) { ADFUtil.invokeEL("#{bindings.Commit.execute}"); FacesContext context = FacesContext.getCurrentInstance(); context.getApplication().getNavigationHandler().handleNavigation(context, null, "submit"); } else if ("no".equals(dialogEvent.getOutcome())) { ADFUtil.invokeEL("#{bindings.Rollback.execute}"); FacesContext context = FacesContext.getCurrentInstance(); context.getApplication().getNavigationHandler().handleNavigation(context, null, "noAction"); } else if ("cancel".equals(dialogEvent.getOutcome())) { FacesContext context = FacesContext.getCurrentInstance(); context.getApplication().getNavigationHandler().handleNavigation(context, null, "cancel"); }


So, the moral of the story is if you just use custom buttons and specify 'none' for type attribute of af:dialog, you won't be able to set focus to any of those buttons. If you use at least button by specifying 'type' to other than 'none', then the focus will be set to the most obvious button among the displayed buttons based on the specified 'type' attribute.

ADF UI - Different ways of invoking or displayinig popup declaratively

To know how to invoke a popup in various ways, please refer to the document on af:popup here. I've created a sample application that demonstrates how to invoke the popup declaratively for different purposes like showing a model popup, displaying note window or panel window, or tooltip text, or menu items, etc. You can download the sample application from here. It's self explanatory.

Below are some sample screen shots.





Enjoy!

Saturday, October 30, 2010

ADF UI - Navigating to next jsf page programmatically (instead of specifying 'action' declaratively)

The below code shows how to programmatically navigate to next jsf page in taskflow. Mainly this is used for conditional navigation i.e., based on some logic we can dynamically decide which page needs to be shown to the user.

public void submitClicked(ActionEvent actionEvent) { navigate(actionEvent, "submit"); }

Code for navigate() method:
protected void navigate(FacesEvent event, String outcome) { RichRegion regionComponent = null; for (UIComponent uic = event.getComponent().getParent(); uic != null; uic = uic.getParent()) { if (uic instanceof RichRegion) { regionComponent = (RichRegion) uic; break; } } if (regionComponent != null) { FacesContext fc = FacesContext.getCurrentInstance(); ExpressionFactory ef = fc.getApplication().getExpressionFactory(); ELContext elc = fc.getELContext(); MethodExpression me = ef.createMethodExpression(elc, outcome, String.class, new Class[] { }); regionComponent .queueActionEventInRegion(me, null, null, false, -1, -1, event.getPhaseId()); } }
In the above code, 'submit' is the 'action' attribute based on which the next jsff page in the flow will be decided.

ADF UI - Refreshing page or region programmatically (instead of partialTriggers) for PPR

To programmatic refresh a component or region in a JSF page,

1. Create a binding for that component. Let's take af:table for example.
<af:table value="#{bindings.DeptVO.collectionModel}" var="row" rows="#{bindings.DeptVO.rangeSize}" emptyText="#{bindings.DeptVO.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.DeptVO.rangeSize}" rowBandingInterval="0" selectedRowKeys="#{bindings.DeptVO.collectionModel.selectedRow}" rowSelection="single" id="t1" width="100%" columnStretching="last" contentDelivery="immediate" autoHeightRows="10" binding="#{pageFlowScope.ExampleBean.tableBinding}" selectionListener="#{bindings.DeptVO.collectionModel.makeCurrent}"> </af:table>


2. In the java bean code, use the below code to refresh the component or region (in our case af:table).
import oracle.adf.view.rich.component.rich.data.RichTable; public class ExampleBean { private RichTable tableBinding; public void sampleMethod(ActionEvent actionEvent) { //Refresh the table AdfFacesContext.getCurrentInstance().addPartialTarget(tableBinding); } public void setTableBinding(RichTable tableBinding) { this.tableBinding = tableBinding; } public RichTable getTableBinding() { return tableBinding; } }

ADF UI - PopupUtil class to show or hide ADF popup programmatically

After ADFUtil class, we need another util class PopupUtil that contains the methods to show/hide ADF popup. Here, I'm including the source of this java file so that it can be handy when needed.

package com.mpapana.common.util.bean; import javax.faces.context.FacesContext; import org.apache.myfaces.trinidad.render.ExtendedRenderKitService; import org.apache.myfaces.trinidad.util.Service; /** * Patterns Utility class for public use. */ public class PopupUtil { /** * Shows the specified popup component and its contents * @param popupId is the clientId of the popup to be shown * clientId is derived from backing bean for the af:popup using getClientId method */ public static void invokePopup(String popupId) { invokePopup(popupId, null, null); } /** * Shows the specified popup and uses the specified hints to align the popup. * @param popupId is the clientId of the popup to be shown - clientId is derived from backing bean for the af:popup using getClientId method * @param align is a hint for the popup display. Check AdfRichPopup js javadoc for valid values. Supported value includes: "AdfRichPopup.ALIGN_START_AFTER", "AdfRichPopup.ALIGN_BEFORE_START" and "AdfRichPopup.ALIGN_END_BEFORE" * @param alignId is the clientId of the component the popup should align to - clientId is derived from backing bean for the component using getClientId method * align and alignId need to be specified together - specifying null for either of them will have no effect. */ public static void invokePopup(String popupId, String align, String alignId) { if (popupId != null) { ExtendedRenderKitService service = Service.getRenderKitService(FacesContext.getCurrentInstance(), ExtendedRenderKitService.class); StringBuffer showPopup = new StringBuffer(); showPopup.append("var hints = new Object();"); //Add hints only if specified - see javadoc for AdfRichPopup js for details on valid values and behavior if (align != null && alignId != null) { showPopup.append("hints[AdfRichPopup.HINT_ALIGN] = " + align + ";"); showPopup.append("hints[AdfRichPopup.HINT_ALIGN_ID] ='" + alignId + "';"); } showPopup.append("var popupObj=AdfPage.PAGE.findComponent('" + popupId + "'); popupObj.show(hints);"); service.addScript(FacesContext.getCurrentInstance(), showPopup.toString()); } } /** * Hides the specified popup. * @param popupId is the clientId of the popup to be hidden * clientId is derived from backing bean for the af:popup using getClientId method */ public static void hidePopup(String popupId) { if (popupId != null) { ExtendedRenderKitService service = Service.getRenderKitService(FacesContext.getCurrentInstance(), ExtendedRenderKitService.class); String hidePopup = "var popupObj=AdfPage.PAGE.findComponent('" + popupId + "'); popupObj.hide();"; service.addScript(FacesContext.getCurrentInstance(), hidePopup); } } /* public static void hideDynamicGlobalPopup(ActionEvent actionEvent) { FacesContext fc = FacesContext.getCurrentInstance(); ExpressionFactory ef = fc.getApplication().getExpressionFactory(); String swapEmptyTaskFlow = "#{bindings.swapEmptyTaskFlow.execute}"; MethodExpression me = ef.createMethodExpression(fc.getELContext(), swapEmptyTaskFlow, String.class, new Class[]{ActionEvent.class}); me.invoke(fc.getELContext(), new Object[] {actionEvent}); if (actionEvent != null) { UIComponent source = (UIComponent)actionEvent.getSource(); UIComponent popup = PatternsUtil.getParentByType(source,RichPopup.class); String popupId = popup.getClientId(fc); PatternsPublicUtil.hidePopup(popupId); } }*/ }


Sample Usages:

i. Displaying the af:popup programmatically
import com.mpapana.bean.util.PopupUtil; public class ExampleBean { private RichPopup confirmationPopup; public void sampleMethod(ActionEvent actionEvent) { PopupUtil.invokePopup(confirmationPopup.getClientId(FacesContext.getCurrentInstance())); } }

ii. Hiding the af:popup programmatically
import com.mpapana.bean.util.PopupUtil; public class ExampleBean { private RichPopup confirmationPopup; public void sampleMethod(ActionEvent actionEvent) { PopupUtil.hidePopup(confirmationPopup.getClientId(FacesContext.getCurrentInstance())); } }


In the above code, the popup 'confirmationPopup' is a binding for af:popup in jsff page (popup code jsff page below).
<af:popup id="p1" binding="#{pageFlowScope.ExampleBean.confirmationPopup}"> <af:dialog id="d1" type="ok" title="Confirmation" inlineStyle="width:340px; height:125.0px;" dialogListener="#{pageFlowScope.ExampleBean.confirmationDialog}"> <af:panelGroupLayout id="pgl2"> <af:outputText value="#{bundle.THE_DETAILS_HAVE_BEEN_SAVED}" id="ot1"/> </af:panelGroupLayout> </af:dialog> </af:popup>


On calling invokePopup() or hidePopup() methods, this popup will be shown or hidden respectively.

ADF UI - ADFUtil class to evaluate, set and invoke EL expressions

ADFUtil.java - The MUST need for any ADF developer. It contains the utility methods to evaluate EL expressions, execute methods specified as EL expressions. Here, I'm including the content of ADFUtil java class so that it can be handy when needed.

package com.mpapana.bean.util; import java.util.Map; import javax.el.ELContext; import javax.el.ExpressionFactory; import javax.el.MethodExpression; import javax.el.ValueExpression; import javax.faces.context.FacesContext; import oracle.adf.model.BindingContext; import oracle.adf.model.DataControlFrame; /** * Provides various utility methods that are handy to * have around when working with ADF. */ public class ADFUtil { /** * When a bounded task flow manages a transaction (marked as requires-transaction,. * requires-new-transaction, or requires-existing-transaction), then the * task flow must issue any commits or rollbacks that are needed. This is * essentially to keep the state of the transaction that the task flow understands * in synch with the state of the transaction in the ADFbc layer. * * Use this method to issue a commit in the middle of a task flow while staying * in the task flow. */ public static void saveAndContinue() { Map sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap(); BindingContext context = (BindingContext)sessionMap.get(BindingContext.CONTEXT_ID); String currentFrameName = context.getCurrentDataControlFrame(); DataControlFrame dcFrame = context.findDataControlFrame(currentFrameName); dcFrame.commit(); dcFrame.beginTransaction(null); } /** * Programmatic evaluation of EL. * * @param el EL to evaluate * @return Result of the evaluation */ public static Object evaluateEL(String el) { FacesContext facesContext = FacesContext.getCurrentInstance(); ELContext elContext = facesContext.getELContext(); ExpressionFactory expressionFactory = facesContext.getApplication().getExpressionFactory(); ValueExpression exp = expressionFactory.createValueExpression(elContext, el, Object.class); return exp.getValue(elContext); } /** * Programmatic invocation of a method that an EL evaluates to. * The method must not take any parameters. * * @param el EL of the method to invoke * @return Object that the method returns */ public static Object invokeEL(String el) { return invokeEL(el, new Class[0], new Object[0]); } /** * Programmatic invocation of a method that an EL evaluates to. * * @param el EL of the method to invoke * @param paramTypes Array of Class defining the types of the parameters * @param params Array of Object defining the values of the parametrs * @return Object that the method returns */ public static Object invokeEL(String el, Class[] paramTypes, Object[] params) { FacesContext facesContext = FacesContext.getCurrentInstance(); ELContext elContext = facesContext.getELContext(); ExpressionFactory expressionFactory = facesContext.getApplication().getExpressionFactory(); MethodExpression exp = expressionFactory.createMethodExpression(elContext, el, Object.class, paramTypes); return exp.invoke(elContext, params); } /** * Sets a value into an EL object. Provides similar functionality to * the <af:setActionListener> tag, except the from is * not an EL. You can get similar behavior by using the following... * setEL(to, evaluateEL(from)) * * @param el EL object to assign a value * @param val Value to assign */ public static void setEL(String el, Object val) { FacesContext facesContext = FacesContext.getCurrentInstance(); ELContext elContext = facesContext.getELContext(); ExpressionFactory expressionFactory = facesContext.getApplication().getExpressionFactory(); ValueExpression exp = expressionFactory.createValueExpression(elContext, el, Object.class); exp.setValue(elContext, val); } }


Sample uses:

i. To evaluate an EL expression.
import com.mpapana.bean.util.ADFUtil; public class ExampleBean { public void sampleMethod() { String lookupName = (String)ADFUtil.evaluateEL("#{pageFlowScope.lookupName}"); } }


ii. To invoke a AMImpl's method added as a methodAction in pageDef file.

a) Invoking a method that takes no parameters
import com.mpapana.bean.util.ADFUtil; public class ExampleBean { public void sampleMethod(ActionEvent actionEvent) { ADFUtil.invokeEL("#{bindings.methodWithoutParams.execute}"); }


b) Invoking a method that takes parameters
import com.mpapana.bean.util.ADFUtil; public class ExampleBean { public void sampleMethod(ActionEvent actionEvent) { ADFUtil.invokeEL("#{bindings.methodWithParams.execute}", new Class[]{String.class,String.class},new Object[]{"First Param","Second Param"} ); } }


iii. To set value for an object which is in the form of EL expression.
import com.mpapana.bean.util.ADFUtil; public class ExampleBean { public void sampleMethod() { ADFUtil.setEL("#{pageFlowScope.showAttendance}","false"); } }

ADF UI - Executing the AMImpl method in UI project's java bean which is added as methodAction in pagedef file

Once we write the methods in AMImpl class and expose them in client's interface, they are available under data controls and can be included as 'methodAction' operation bindings in the pagedef files of JSF pages. Now, the question is how to execute this method binding in java bean. The method can be simple void method without any arguments, or it can have arguments with some return type. Below is the sample code to execute AMImpl's method(which is added as methodAction in pagedef file) in java beean.

public void saveSections(ActionEvent ae) { DCBindingContainer bindings = (DCBindingContainer)ADFUtil.evaluateEL("#{bindings}"); //method without parameters OperationBinding opBinding = bindings.getOperationBinding("saveClassSections"); opBinding.execute(); if (opBinding.getErrors().size() == 0) { PopupUtil.invokePopup(confirmationPopup.getClientId(FacesContext.getCurrentInstance())); } } public void classChanged(ValueChangeEvent valueChangeEvent) { DCBindingContainer bindings = (DCBindingContainer)ADFUtil.evaluateEL("#{bindings}"); //method that takes one parameter OperationBinding opBinding = bindings.getOperationBinding("setNoOfPeriods"); opBinding.getParamsMap().put("classCode",valueChangeEvent.getNewValue()); opBinding.execute(); noOfPeriodsDisabled = false; sectionsTableRendered = false; }

Please not that the OperationBinding that we need to import for the above method code is:
import oracle.binding.OperationBinding;


The above two AM methods "saveClassSections" and "setNoOfPeriods" are be included in pagedef as methodAction bindings as below:
<methodAction id="saveClassSections" InstanceName="GreatSchoolsAMDataControl.dataProvider" DataControl="GreatSchoolsAMDataControl" RequiresUpdateModel="true" Action="invokeMethod" MethodName="saveClassSections" IsViewObjectMethod="false"/> <methodAction id="setNoOfPeriods" InstanceName="GreatSchoolsAMDataControl.dataProvider" DataControl="GreatSchoolsAMDataControl" RequiresUpdateModel="true" Action="invokeMethod" MethodName="setNoOfPeriods" IsViewObjectMethod="false"> <NamedData NDName="classCode" NDType="java.lang.String"/> </methodAction>


The actual definitions for these methods in AMImpl are as below and these are exposed in client's interface.

public void saveClassSections() { ViewObjectImpl tVO = getClassSubjectTeacherTVO(); String classCode = (String)tVO.getCurrentRow().getAttribute("ClassCode"); ViewObjectImpl csVO = getClassSectionVO(); ViewObjectImpl lookupsPVO = getLookupsPVO(); Row[] sections = lookupsPVO.getAllRowsInRange(); for (int i = 0; i < sections.length; i++) { Row section = sections[i]; if ((Boolean)section.getAttribute("selected")) { Row csRow = csVO.createRow(); csRow.setAttribute("ClassCode", classCode); csRow.setAttribute("SectionCode", section.getAttribute("LookupCode")); csVO.insertRow(csRow); } } this.getDBTransaction().commit(); } public void setNoOfPeriods(String classCode) { ViewObjectImpl tVO = getClassSubjectTeacherTVO(); ViewObjectImpl csVO = getClassSectionVO(); csVO.setApplyViewCriteriaName("findByClassCode"); csVO.setNamedWhereClauseParam("Bind_ClassCode", classCode); csVO.executeQuery(); csVO.setRangeSize(-1); if (csVO.getAllRowsInRange().length > 0) { tVO.getCurrentRow().setAttribute("NoOfPeriods", csVO.first().getAttribute("NoOfPeriods")); } else{ tVO.getCurrentRow().setAttribute("NoOfPeriods",null); } }


There is another way of invoking AMImpl's methods from Java bean using EL expressions using the util methods in ADFUtil class. ADFUtil class and using the util methods in it has been explained in this post.

ADF Model: Programmatically executing view criteria in AMImpl method

This is the most common requirement to execute view criteria of a VO in AMImpl (application module's Impl class). Here is the sample code:

public void getEmployeesInDept(Integer deptId) { if (deptId == null || deptId == 0) { ViewObjectImpl empDeptVO = this.getEmpDeptVO(); //View Criteria without bind variable ViewCriteria vc = empDeptVO.getViewCriteria("noRowsVC"); empDeptVO.applyViewCriteria(vc); empDeptVO.executeQuery(); } else { ViewObjectImpl empDeptVO = this.getEmpDeptVO(); //View Criteria with bind variable 'Bind_deptId' empDeptVO.setApplyViewCriteriaName("findByDeptId"); empDeptVO.setNamedWhereClauseParam("Bind_deptId", deptId); empDeptVO.executeQuery(); } }


The above method shows how to execute view criterias "noRowsVC"(with no bind variables) and "findByDeptId"(which has a bind variable "Bind_deptId") in EmpDeptVO. The below screenshots show the view criterias in EmpDeptVO.

That's it. The method executes the VC and gets the query results. You can expose this method in client's interface and add this method in jsff pagedef file as a methodAction binding. The later post shows how to pass parameters and call/execute this AMImpl method ( i.e., getEmployeesInDept) in UI project's java bean.

Thursday, October 28, 2010

ADF UI - Accessing properties file or resource bundle in java bean

If we directly refer to resource bundle in jsf page by using 'Select text resource..' option available, the c:set tag will be added to the jsff page and we'll use the bundle properties using it's 'var' as a reference.
<?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" xmlns:c="http://java.sun.com/jsp/jstl/core"> <c:set var="demouiBundle" value="#{adfBundle['com.demo.ui.DemoUiBundle']}"/> <af:panelGroupLayout id="pgl1"> <af:showDetailHeader text="#{demouiBundle['Header.Title']}" disclosed="true" id="sdh1"> <f:facet name="context"/> <f:facet name="menuBar"/> <f:facet name="toolbar"/> <f:facet name="legend"/> <f:facet name="info"/> </af:showDetailHeader> <af:showDetailHeader text="#{pageFlowScope.ExampleBean.headerTitle}" disclosed="true" id="showDetailHeader1"> <f:facet name="context"/> <f:facet name="menuBar"/> <f:facet name="toolbar"/> <f:facet name="legend"/> <f:facet name="info"/> </af:showDetailHeader> </af:panelGroupLayout> </jsp:root>


But, some times there might be needs to refer the properties bundle from java bean to access a property.

This can be done by using the below code.
package com.demo.beans; import java.util.ResourceBundle; import oracle.javatools.resourcebundle.BundleFactory; public class ExampleBean { public String getHeaderTitle() { ResourceBundle rs = BundleFactory.getBundle("com.demo.ui.DemoUiBundle"); return rs.getString("Header.Title"); } }


Content of the sample bundle file:
# sample properties Header.Title=Employee Details EMP_NAME=Employee Name DEPT_NAME=Department Name


Running the above jsff, we'll get the same text by using java bean as well direct reference in jsff (Screen shot below).

ADF UI: Pagination (Navigation using First, Previous, Next, Last buttons) using <af:iterator>

It was a tough time for me to achieve pagination (i.e., navigating through a set of records using First, Previous, Next, Last buttons) in one of my ADF pages. I searched in net for any solution, but finally convinced that there is no declarative way to achieve this functionality in ADF. And, I also found that this concept of pagination was declarative in OAF and this had been replaced by ADF table scroll navigation. So, I went ahead and did some research and developed my own pagination technique using <af:iterator>. The below steps explain the use case and how to achieve the same.

Sample Use Case:
For example, we have a list of employees (say 50) and we want to display 5 employees at a time and on clicking 'Next' button we want to display next 5 records and so on. Similarly, on clicking 'Previous' button we want to display previous 5 records. The 'First' and 'Last' buttons should take use to the first 5 records and last 5 records respectively.
One more challenge to this use case is that we want to disable the 'Previous' button, if there are no more previous records and vice versa. Similarly, we want to disable the 'First' and 'Last' buttons if the current list is first set or last set of records respectively. Achieving this would be little tricky and we'll see how we can implement it.

Implementation Steps:
Here, I'm going to use <af:iterator> component to iterate through list of employee records and display the sets of records. IF you want to know how to use af:iterator, read my previous post.

1. First, tune the VO which you want to iterate. Tuning the VO will give us better performance if we tune as per our page range size requirements in UI (Screenshot below).


2. Specify the 'rangeSize' for the VOIterator in pageDef as the no. of rows you want to display for each time you click on 'Previous' and 'Next'.  This represents rowSet size (In the below code, I'm setting it to 5).
<iterator Binds="EmpDeptVO" RangeSize="5" DataControl="DemoAMDataControl" id="EmpDeptVOIterator" ChangeEventPolicy="ppr"/>


3. Drop af:iterator into jsff and add all VO attributes under iterator (refer to previous post). Please find the below code. Here, in pagination, the attribute 'first' of af:iterator will play a major role. It'll actually specify starting from which row number, the data is to be displayed. By default, let's keep it 0. Later, we'll update it to move back and forth on clicking 'Previous' and 'Next' buttons.
<af:iterator id="i1" value="#{bindings.EmpDeptVO.collectionModel}" var="row" varStatus="i" rows="#{bindings.EmpDeptVO.rangeSize}" binding="#{pageFlowScope.ExampleBean.employeeIterator}">


4. Add buttons 'First', 'Previous', 'Next' and 'Last' and specify the actionListeners as below.

<af:toolbar id="t1" partialTriggers="i1"> <af:commandToolbarButton text="First" id="ctb2" actionListener="#{pageFlowScope.ExampleBean.firstClicked}" disabled="#{pageFlowScope.ExampleBean.previousDisabled}"/> <af:commandToolbarButton text="Previous" id="ctb1" actionListener="#{pageFlowScope.ExampleBean.PreviousClicked}" disabled="#{pageFlowScope.ExampleBean.previousDisabled}"/> <af:commandToolbarButton text="Next" id="commandToolbarButton1" actionListener="#{pageFlowScope.ExampleBean.nextClicked}" disabled="#{pageFlowScope.ExampleBean.nextDisabled}"/> <af:commandToolbarButton text="Last" id="ctb3" actionListener="#{pageFlowScope.ExampleBean.lastClicked}" disabled="#{pageFlowScope.ExampleBean.nextDisabled}"/> </af:toolbar>


public void firstClicked(ActionEvent actionEvent) { UIXIterator empIterator = getEmployeeIterator(); empIterator.setFirst(0); AdfFacesContext.getCurrentInstance().addPartialTarget(empIterator); } public void PreviousClicked(ActionEvent actionEvent) { UIXIterator empIterator = getEmployeeIterator(); int first = empIterator.getFirst() - empIterator.getRows(); empIterator.setFirst(first); AdfFacesContext.getCurrentInstance().addPartialTarget(empIterator); } public void nextClicked(ActionEvent actionEvent) { UIXIterator empIterator = getEmployeeIterator(); int first = empIterator.getFirst() + empIterator.getRows(); empIterator.setFirst(first); AdfFacesContext.getCurrentInstance().addPartialTarget(empIterator); } public void lastClicked(ActionEvent actionEvent) { UIXIterator empIterator = getEmployeeIterator(); int noOfRows = empIterator.getRowCount(); int first = noOfRows- noOfRows%empIterator.getRows(); empIterator.setFirst(first); AdfFacesContext.getCurrentInstance().addPartialTarget(empIterator); }

5. And to achieve enabling/disabling the buttons, I wrote the below method based on the total no. of rows and current row set.
public boolean isPreviousDisabled() { UIXIterator empIterator = getEmployeeIterator(); int first = empIterator.getFirst(); if (first <= 0) { return true; } return false; } public boolean isNextDisabled() { UIXIterator empIterator = getEmployeeIterator(); int first = empIterator.getFirst() + empIterator.getRows(); int noOfRows = empIterator.getRowCount(); if (first >= noOfRows) { return true; } return false; }


Now, run the page and you'll see first 5 records and you can navigate to different row sets using the navigation buttons. Sample screen shots below:


Related Posts with Thumbnails