Sunday, 17 December 2017

Oracle ADF How to call a method on Page Load

Oracle ADF How to call a method on Page Load 

In this example we will see how we can call a method inside your backing bean or application module when page is getting loaded.
The scenario I am taking here is you have to show the current row index of the view object row on page load in ADF table column.
1) Create a view object based on Job table in HR schema with name OnPageLoadJobVO. Do generate the VOImpl and VORowImpl java classes for the created view object.
2) Add a transient variable inside your view object with name
“myCurrentRowIndex”


3) Add the above created view Object inside your application module.
4) Add the following code inside your application module Impl class.
public void setJobCurrentRowIndex() {
System.out.println("I am here inside AM");
OnPageLoadJobVOImpl vo = getOnPageLoadJobVO1();
OnPageLoadJobVORowImpl row=null;
long fetchedRowCount = vo.getEstimatedRowCount();
RowSetIterator jobVoIter = vo.createRowSetIterator("jobVoIter");
if (fetchedRowCount > 0)
{
jobVoIter.setRangeStart(0);
jobVoIter.setRangeSize((int)fetchedRowCount);
for (int count = 0; count < fetchedRowCount; count++)
{
row=(OnPageLoadJobVORowImpl)jobVoIter.getRowAtRangeIndex(count);
row.setmyCurrentRowIndex(new Number(count));
}
}
jobVoIter.closeRowSetIterator();

5) Expose the above created method in application module to the client.
6) Drag and drop the OnPageLoadJobVO object to your jspx page as ADF read only table.
7) Create a new Managed bean using faces-config.xml with
name= OnPageLoadBackingBean and
class= OnPageLoadBean
8) Copy and paste the following code inside your newly created managed bean class.
package viewlayer.backing;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import model.common.AppModule;
import oracle.adf.controller.v2.lifecycle.Lifecycle;
import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;

public class OnPageLoadBean implements PagePhaseListener{
public OnPageLoadBean() {
}
public void afterPhase(PagePhaseEvent event) {
}
public void beforePhase(PagePhaseEvent event) {
if (event.getPhaseId() == Lifecycle.PREPARE_MODEL_ID) {
if (!isPostback())
/*
System.out.println("i am here inside backing bean");
this.getApplicationModule().setJobCurrentRowIndex();
}
}
private boolean isPostback() {
return Boolean.TRUE.equals(resolveExpression("#{adfFacesContext.postback}"));
}
private Object resolveExpression(String expression) {
FacesContext ctx = FacesContext.getCurrentInstance();
Application app = ctx.getApplication();
ValueBinding bind = app.createValueBinding(expression);
return bind.getValue(ctx);
}
private AppModule getApplicationModule() {
return (AppModule)resolveExpression ("#data.AppModuleDataControl.dataProvider}");
}
}
9) Right click on your jspx page and go to your page definition. Where set the attribute ControllerClass inside your pageDefinition tag as the name of the managed bean.

 
Run your page to check the results.
ADF: Execute code before page load
Problem description: In ADF there are multiple ways to execute a piece of code before page load. In this blog I would like to point out those options and their usecases.

Solution
1. If you have a Bounded task-flow and a pagefragment. You want to run a piece of code before fragment load and you want to do it only once. It means if you take any action on your page you do not want to run that piece of code again.

In such case you can simply add a method activity before your page in task-flow. Such method activities gets executed only once before page load.

While writing such methods, you will not have any handle for page components and its binding because you have not yet reached to your page.

Generally we use this approach when we want to do something model side for example I want to execute a VO and before showing its data on page. We can write method in AMImpl, expose it and drop it before fragment on task-flow.


2. If you want a piece of code to get executed anytime you take action on your page. You don't want page components but you want bindings to be available. 

In such case you can add such methods and operation binding in pageDef file and also add corresponding executable. Set executable's refresh property as per your need. Also placing of this executable binding is important as it gets executed from top to bottom. If you expect some VO to have data then you may want to keep this executale binding after that.

If its a bean method that you want to execute in this approach, I would suggest you to write a separate class and keep only that method in that java class and expose it as datacontrol. In any way you are not going to get page components. Getter/Setter of bean will return null so no point in mixing bean with this method. Keep bean and this Java method separately. Actually its not a bean method you are invoking but a datacontrol pojo kind of method.

If you keep your method in different java file but you want to access your bean. You can invoke ADFUtils method to get beanscope first and then get bean instance.


3. If you want a piece of code to get executed anytime you take action on your page. You also want page components to be available. 

In such case you should add hidden outputtext (visible=false) to your page and bind it with bean. In its getter/setter method you can place your code. Keep outputText at the bottom of your page so that by the time its getter/setter runs all other page components are already bound to bean.



In approach 2 and 3 although your method will get execute everytime you make server request but you can control its behaviour by keeping a variable in pageFlowScope and set its value to Y when method got executed first time and next time ownwards check if pageFlowScope variable value is Y then only execute. Something like below

public void initMyPage(){

     String value = ADFUtils.getPageflowScope().get("isPageAlreadyInitialised");

    if(!"Y".equals(value)){

           //write your initialization code here. This piece will get executed only once.

         ADFUtils.getPageFlowScope().put("isPageAlreadyInitialised", "Y");


   }

}

4. Using PagePhaseListener: If you are working with jspx page you can have PhaseListeners as well. Here challenge is carefully picking the phase in which you want to execute your code.

You can use ViewScope variable if you want to make sure code only runs on very first load of page.
@Override
    public void afterPhase(PagePhaseEvent pagePhaseEvent) {

        if (pagePhaseEvent.getPhaseId() == Lifecycle.INIT_CONTEXT_ID) {
                
           String value = ADFUtils.getViewScope().get("isPageAlreadyInitialised");

             if(!"Y".equals(value)){

                        initMyPage();

                      ADFUtils.getViewScope().put("isPageAlreadyInitialised", "Y");

            }
         }
    }



As bindings are already in place so you should be able to use bindingContext and bindingContainer to use bindings. Also to get UI component you can get handle of bean and get getter for bean. To get bean handle you can use expressions like
MyBean myBean = ADFUtils.evalueageEL("#{backingBeanScope.myBean}");


 How to call a method when the page loads"
 Quite often, there comes a question in OTN, with different subjects, all meaning "How to call a method when my ADF page loads?". More often, people tend to take the approach of ADF Phase Listener by overriding before/afterPhase methods.

In this blog, we will go through different options in achieving it.

1. Method Call Activity as default activity in Taskflow :

If the application is built with taskflows, then this is the best suited approach to take. 

1.a. Calling a Data Control Method :

To call a Data Control method (ex: A method in AMImpl exposed as client interface), simply Drag and Drop the method as Default Method Call Activity, then draw a control flow case from the method to your page. 

Once after this, drop the taskflow as region in main page. When we run the main page, the Method Call Activity would be called first, and then the page will be rendered.

1.b. Calling a Method in Backing Bean:


To call a method in the backing bean before pageload, we can follow the similar approach as above. Instead of binding the Method Call Activity to an action/method binding in pagedef, we bind to the method. Insert a Method Call Activity (and make it as default) from the Component Palette. Double click on to select a method to bind.


This approach can also be used, to perform some action in backing bean along with calling a method Data Control (just need to add bindings code in backing bean to execute DC method).


2. Using invokeAction Executable :

If the application is built with pages and no taskflows are involved, then this option can be taken into consideration.

In the page definition of the page, add an invokeAction Executable and bind it to the method needed to be executed.


3. Using combination of Server and Client Listeners :

If the page does not have any page definition, then to call a method in backing bean, this approach can be taken. In this, a serverListener would be added at the document level, which would be calling the method in backing bean. Along with this, a clientListener would be added with "load" type (i.e will be triggered when the page loads), which would queue a serverEvent to trigger the method.


4. Using Page Phase Listener :

This should be the last resort. Care should be taken when using this approach since the Phase Listener would be called for each request sent by the client. 


How to call a method on Page Load
ADF : How to call a method on Page Load ?

Use case : We have a jspx page welcome.jspx. We have to call a custom method(onPageLoadWelcome) before loading the jspx page.
  private void onPageLoad(PhaseEvent phaseEvent){
    System.out.println("Called onPageLoad by Neelmani");
    }
    
NOTE: Some times afterPhase() and beforePhase() will get executed multiple times. How to restrict them?
Solution:
    public void beforePhase(PhaseEvent phaseEvent){
     
        if(phaseEvent.getPhaseId().toString().equalsIgnoreCase("RENDER_RESPONSE 6")){
            onPageLoad();
            }
        }
  
    public void onPageLoad(){
        System.out.println("onPageLoad");
        }

 

No comments:

Post a Comment