Thursday Jan 23, 2014

Disbling buttons when new record is created in ADF form

In some cases, we might want to disable some of the buttons / items in the page (say navigation buttons), when a new record is created in the form. 

To achieve this, we would start from Sireesha's excellent blog : https://blogs.oracle.com/adf/entry/highlighting_new_rows_in_adf  . In this blog, insight is given on entityState of a row. 

Before starting, assuming that we have a Form (Say based on Employees), with navigation, create, commit and rollback buttons.

Our usecase is to disable the navigation buttons when the user clicks on CreateInsert button. For this, we could set an EL to the disabled property of the navigation buttons.

Code snippet of the First button.

<af:commandButton actionListener="#{bindings.First.execute}"
                                  text="First"
                                  disabled="#{bindings.Empno.currentRow.entities[0].entityState == 0 or !bindings.First.enabled}"
                                  partialSubmit="true" id="cb2"/>

Here Empno refers to the one of the attribute binding of the form (Primary Key attribute to be precise).

This approach disables the buttons when the form goes to Insert mode (refer the blog mentioned above to find out more about entityState EL).



Monday Nov 25, 2013

ADF Faces Layout Basics - New Whitepaper Released

As the title suggests, basics of layout components available in ADF Faces is covered in this whitepaper.

This paper can be accessed from this location : http://www.oracle.com/technetwork/developer-tools/adf/learnmore/adffaceslayoutbasics-2046652.pdf

In a nutshell, this paper talks about

  • different layout components available in ADF
  • their geometry management
  • example situations during which a particular component can be used
  • reverse engineering some of the popular websites and
  • solutions to few frequent layout scenarios

This paper will be very helpful for understanding different layout components offered by ADF Faces and also to use them effectively to build your website.

You can also browse through ADF Architecture Square Homepage to find this and other related whitepapers : http://bit.ly/adfarchsquare

Wednesday Oct 23, 2013

Building Tag Cloud Declarative ADF Component

When building a website, there could a requirement to add a tag cloud to let the users know the popular tags (or terms) used in the site. In this blog, we would build a simple declarative component to be used as tag cloud in the page.

To start with, we would first create the declarative component, which could display the tag cloud. We will do that by creating a new custom application from the new gallery.

Give a name for the app and the project and from the new gallery, let us create a new ADF Declarative Component

We need to specify the name for the declarative component, attributes in it etc. as follows


For displaying the tags as cloud, we need to pass the content to this component. So, we will create an attribute to hold the values for the tag. Let us name it as "value" and make it as java.lang.String  type.

Once after this, to hold the component, we need to create a tag library. This can be done by clicking on the Add Tag Library button.

Clicking on OK buttons in all the open dialogs would create a declarative component for us. Now, we need to display the tag cloud based on the value passed to the component. To do that, we assume that the value is a Tree Binding and has two attributes in it, say "Name" and "Weight". To make a tag cloud, we would put together the "Name" in a loop and set it's font size based on the "Weight". After putting our logic to work, here is how the source look

Attributes added to the declarative components can be retrieved by using #{attrs.<attribute_name>}. Now, we need to deploy this project as ADF Library Jar file, so that this can be distributed to the consuming applications.

We'll select ADF Library Jar as type and create the profile.

We would be getting the jar file after deployment.

To test the functionality, we could create a simple Fusion Web Application.

To add our custom component to the consuming application, we can create a file system connection pointing to the location where the jar file is and add it or, add through the project properties of the ViewController project.

Now, our custom component has been added to the consuming application. We could test that by creating a VO in the model project with a query like,

select 'Faces' as Name,25 as Weight from dual
union all
select 'ADF', 15 from dual  
union all
select 'ADFdi', 30 from dual
union all
select 'BC4J', 20 from dual
union all
select 'EJB', 40 from dual
union all
select 'WS', 35 from dual

Add this VO to the AppModule, so that it would be exposed to the data control. Then, we could create a jspx page, and add a tree binding to the VO created.

We can now see our Tag Cloud declarative component is available in the component palette.


 It can be inserted from the component palette to our page and set it's value property to CollectionModel of the tree binding created.

Now that we've created the Declarative component and added that to our page successfully, we can run the page to see how it looks.

As per the query, the Tags are displayed in different fonts, based on their weight.

Monday Nov 26, 2012

Restrict number of characters to be typed for af:autoSuggestBehavior

When using AutoSuggestBehavior for a UI Component, the auto suggest list is displayed as soon as the user starts typing in the field. In this article, we will find how to restrict the autosuggest list to be displayed till the user types in couple of characters.

This would be more useful in the low latency networks and also the autosuggest list is bigger. We could display a static message to let the user know that they need to type in more characters to get a list for picking a value from. Final output we would expect is like the below image

FinalOutput1.jpgFinalOutput2.jpg

Lets see how we can implement this. Assuming we have an input text for the users to enter the country name and an autosuggest behavior is added to it.

<af:inputText label="Country" id="it1">
                    <af:autoSuggestBehavior />
                </af:inputText>
Also, assuming we have a VO (we'll name it as CountryView for this example), with a view criteria to filter out the VO based on the bind variable passed.

VC.jpg

Now, we would generate View Impl class from the java node (including bind variables) and then expose the setter method of the bind variable to client interface.

In the View layer, we would create a tree binding for the VO and the method binding for the setter method of the bind variable exposed above, in the pagedef file

Pagedef.jpg

As we've already added an input text and an autosuggestbehavior for the test, we would not need to build the suggested items for the autosuggest list.Let us add a method in the backing bean to return us List of select items to be bound to the autosuggest list.

 padding: 5px; background-color: #fbfbfb; min-height: 40px; width: 544px; height: 168px; overflow: auto;">        public List onSuggest(String searchTerm) {
        ArrayList<SelectItem> selectItems = new ArrayList<SelectItem>();
        if(searchTerm.length()>1) {
        //get access to the binding context and binding container at runtime
        BindingContext bctx = BindingContext.getCurrent();
        BindingContainer bindings = bctx.getCurrentBindingsEntry();
        //set the bind variable value that is used to filter the View Object
        //query of the suggest list. The View Object instance has a View
        //Criteria assigned
        OperationBinding setVariable = (OperationBinding) bindings.get("setBind_CountryName");
        setVariable.getParamsMap().put("value", searchTerm);
        setVariable.execute();
        //the data in the suggest list is queried by a tree binding.
        JUCtrlHierBinding hierBinding = (JUCtrlHierBinding) bindings.get("CountryView1");
                                                                                                                                              
        //re-query the list based on the new bind variable values hierBinding.executeQuery();
        //The rangeSet, the list of queries entries, is of type
        //JUCtrlValueBndingRef.
        List<JUCtrlValueBindingRef> displayDataList = hierBinding.getRangeSet();
        
        for (JUCtrlValueBindingRef displayData : displayDataList){
            Row rw = displayData.getRow();
            //populate the SelectItem list
            selectItems.add(new SelectItem(
            (String)rw.getAttribute("Name"),
            (String)rw.getAttribute("Name")));
            }
        
        }
        else{
            SelectItem a = new SelectItem("","Type in two or more characters..","",true);
            selectItems.add(a);
        }
        return selectItems;
    }

So, what we are doing in the above method is, to check the length of the search term and if it is more than 1 (i.e 2 or more characters), the return the actual suggest list. Otherwise, create a read only select item

new SelectItem("","Type in two or more characters..","",true);

and add it to the list of suggested items to be displayed. The last parameter for the SelectItem (boolean) is to make it as readOnly, so that users would not be able to select this static message from the displayed list.

Finally, bind this method to the input text's autosuggestbehavior's suggestedItems property.

<af:inputText label="Country" id="it1">
        <af:autoSuggestBehavior
              suggestedItems="#{AutoSuggestBean.onSuggest}"/>
 </af:inputText>

Friday Aug 10, 2012

How to create multi level cascading (dependent) list of values using BC4J

There are quite a number of documentations / blogs on creating cascading (dependent) list of values in ADF Application using BC4J as model.

Some examples

1. https://blogs.oracle.com/shay/entry/got_to_love_cascading_lovs_in

2. http://mjabr.wordpress.com/2011/04/01/cascade-list-of-values/

However, these entries talk about creating list of values for master-detail attributes. In this article, I would be explaining about creating list of values which have master-detail-grand detail relationship (though the implementation is same, the use case is different).

Assuming that we've our model ready, with an VO for Person (based on Person EO), Country, State and City (read-only VOs).

Now, we'll modify the query of the dependent VOs to include a bind variable in their where clause.

We could then create List of Values for the CountryId, StateId and CityId attributes in the Person VO.

Above image shows an example for creating LOV for the CityId attribute. LOV is created for the CountryId and StateId attributes in the similar fashion.

Now that we've created LOV for all the 3 attributes, we need to pass the required values for the bind variable we created previously (in State and City VOs). That is done from the View Accessors tab of Person VO.

Now we are set to go with the dependent LOV. Before running the Tester, we need to make the CountryId and StateId attributes automatically submit their values upon change (by setting Auto Submit UI Hint to true)

 (Above image shows setting Auto Submit UI Hint to true for CountryId attribute. In the same manner, set the Auto Submit UI Hint to true for the StateId attribute as well).

and add make StateId dependent on CountryId, CityId to depend on CountryId and StateId.


We are set to go now. Run the AppModule tester to verify the values.

When using this in jspx page, set the AutoSubmit properties for the CountryId and StateId components, and add the partialTriggers for StateId and CityId components with the Id of CountryId component, as shown below.

Hint: Right click on the images and select View Image to view it completely if it is cropped.

Tuesday Jul 17, 2012

Interpret af:query's queryEvent and display popup to end user using QueryListener

Found an interesting question on OTN. Based on the question, wired a usecase to try out.

Usecase : Show a warning to user when they try to search the records (af:query component), without specifying a criteria / a wild card "%". I.e, when the user tries to query the entire table, show a warning that querying all the records would take some time.

There are three phases in implementing this usecase.

1. Interpret the query event and get the query criteria.
2. Show the popup.
3. Process the interpreted query based on the outcome of the popup.

Before proceeding with the implementation, we'll create a page for assumption.
a. Page contains a af:query component with a resultant table / read-only table.
b. Has a popup to be shown to the end user.
c. Bound to a bean.

We'll now implement it phase by phase.

First of all, we'll create couple of attributes in the bean and generate accessors to them.
    private RichTable empTable;
    private boolean warnUser=true;
   // Set the default queryListener property value of the af:query component
   // for mexpr.
   private String mexpr = "#{bindings.ImplicitViewCriteriaQuery.processQuery}"; 
    private QueryEvent qEvt;

    public void setEmpTable(RichTable empTable) {
        this.empTable = empTable;
    }

    public RichTable getEmpTable() {
        return empTable;
    }



Now, we need to trap the query event of the af:query component to perform the desired task. We can add a queryListener and bind it to the af:query component

  public void processQuery(QueryEvent queryEvent) {
        // store the queryEvent in a bean attribute, to be used in another method.
        qEvt = queryEvent;
        // Reset the flag. This flag would be used to check if the system has to 
        //raise the popup or not
        warnUser=false;
        DCBindingContainer bc =   
        (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();   
         // Get the view criteria that would be applied.
         // findExecutableBinding method takes two parameters.
         // id of the searchRegion executable binding
         // criteria for the searchRegion executable binding
         // Ex : <searchRegion Criteria="__ImplicitViewCriteria__"      
         // Customizer="oracle.jbo.uicli.binding.JUSearchBindingCustomizer"
         //         Binds="EmpView1Iterator" id="ImplicitViewCriteriaQuery"/>
       


 ViewCriteria vc = 
JUSearchBindingCustomizer.getViewCriteria((DCBindingContainer)bc.findExecutableBinding("ImplicitViewCriteriaQuery"),"__ImplicitViewCriteria__");


        ViewCriteriaRow vcr = (ViewCriteriaRow)vc.get(0);
        // Some logic to set the flag. Here, checking if the Ename attribute has
        // no value specified / used a wildcard expression ("%").
        for(int i=0;i<vcr.getAttributeNames().length;i++) {
           
 if(vcr.getAttributeNames()[i] == "Ename" &&  
("%".equals(vcr.getAttributeValues()[i]) || 
vcr.getAttributeValues()[i]==null)) 
                warnUser=true;
        }
 
        if(warnUser) 
            showPopup();
        else 
            executeQuery();
 
    }



showPopup and executeQuery are custom methods to show the popup and to process the query respectively.

       public void showPopup(){
        UIViewRoot root = FacesContext.getCurrentInstance().getViewRoot();
        RichPopup popup = (RichPopup) root.findComponent("p1");
        RichPopup.PopupHints hints = new RichPopup.PopupHints();
        popup.show(hints);
    }

  // This method invokes the method expression used by af:query component programatically
    public void executeQuery(){
        processMethodExpression(mexpr, new Object[] {qEvt}, new Class[] {QueryEvent.class}); 
        AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance(); 
        adfFacesContext.addPartialTarget(empTable); 
 
    }

    private Object processMethodExpression(String methodExpression, Object[] parameters, Class[] expectedParamTypes) { 
        FacesContext fctx = FacesContext.getCurrentInstance(); 
        ELContext elctx = fctx.getELContext(); 
        Application app = fctx.getApplication(); 
        ExpressionFactory exprFactory = app.getExpressionFactory(); 
       
 MethodExpression methodExpr = exprFactory.createMethodExpression(elctx,
 methodExpression, Object.class, expectedParamTypes); 
        return methodExpr.invoke(elctx, parameters); 
        }


Now, we need to bind the custom querListener created above to the af:query component

  <af:query id="qryId2" headerText="Search" disclosed="true"
      value="#{bindings.ImplicitViewCriteriaQuery.queryDescriptor}"
      model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
      queryListener="#{viewScope.QueryBean.processQuery}".....



We are almost there. Now, when we run the page and query for the records by keeping Ename as null (in the query panel), we would get the popup.

Final step is to handle the user action on the popup and then proceed executing the query / to stop it.

For this, we'll create a dialog listener and bind it to popup.

 public void onDialog(DialogEvent dialogEvent) {
        Outcome o = dialogEvent.getOutcome();
        if(o == Outcome.yes) 
            executeQuery();
 
    }

                <af:popup childCreation="deferred" autoCancel="disabled" id="p1">
                    <af:dialog id="d2" type="yesNo" title="Are you sure?"
                               dialogListener="#{viewScope.QueryBean.onDialog}">
                       
 <af:outputText value="It would be time consuming to query for all 
records. Are you sure you want to continue?" id="ot9"/>
                        <f:facet name="buttonBar"/>
                    </af:dialog>
                </af:popup>



Here is how the runtime would be.

step1.jpg



Enter % for Ename and hit the Search button

step2.jpg



Popup with a warning message displayed

step3.jpg


Clicking on Yes / No on the popup performs respective task (perform query / cancel query).

step4.jpg



Thursday Jul 12, 2012

Calculating time since last query in adf using java script

In some scenarios, we would need to calculate the time elapsed since the last query execution, without contacting the server (i.e without using af:poll component). This blog is to give an example for this scenario by using javascript.

OTN question : https://forums.oracle.com/forums/thread.jspa?forumID=83&threadID=2411997

Assuming that we have an application with a table in the page, and a commandButton to query the records, we will proceed further to build our usecase.

The logical flow of this implementation would be like,
1. Execute the query
2. When the button clicked, reset the previous time
3. Set the time to 0.
4. Trigger the timer to increment time.

Now, implementation comes.

Lets add an output text to display the time lapsed

<af:outputLabel value="Minutes since last query :" id="ol1"/>
        <af:outputText value="" id="ot9" clientComponent="true"/>

Since we would be accessing the output text from javascript, we would need to make it available to client by setting clientComponent to true.

We now need to add a javascript code to our page, to count the minutes and display in the output text.

        <af:resource type="javascript">

        function updateTimer(){
            var opTxt = AdfPage.PAGE.findComponentByAbsoluteId('ot9');
            var curMins = opTxt.getProperty('value');
            if(curMins == '' || curMins == null)
                opTxt.setProperty('value','0');               
            else
                opTxt.setProperty('value',parseInt(opTxt.getProperty('value')) + 1);
        }

        </af:resource>

In the above code, we find the outputText component (with id ot9), get its value. If the value is blank or null, then set it to 0. If it is a non-zero number, then increment it.

Now, we need to call this method every minute to update the output text. For this, we will add another JS function.

        function startTimer(){
            var opTxt = AdfPage.PAGE.findComponentByAbsoluteId('ot9');
            opTxt.setProperty('value','');
            updateTimer();
            var int=window.setInterval("updateTimer()",60000);
        }

This function finds the outputText (with id ot9), resets its value (i.e set it to blank) and then schedules the function we've created first (updateTimer()) to be called every minute (i.e 60000 milliseconds).

Assuming we have a button to execute the query, we'll add a client listener to the button to execute this function when pressed.

                <af:commandButton actionListener="#{bindings.Execute.execute}" text="Execute"                                       
                                        disabled="#{!bindings.Execute.enabled}"  id="cb1" partialSubmit="true">
                          <af:clientListener method="startTimer" type="action" />
                </af:commandButton>

Ouput would be like below.


Monday May 21, 2012

Executing Put operation of REST service programatically from ADF App

In quite some cases, we would like to call the PUT method on a REST service by constructing the parameters during runtime and pass it on. In this article, we would go through how to deal with such cases when building an ADF Application.

Refer this tutorial for introduction to REST service in where, GET and DELETE methods are explained. In this sample, we'll see how to implement PUT operation using a HashMap. In fact, as like in the above tutorial, we can directly execute the PUT method as well. This article is mainly concentrated on how to construct the parameters dynamically at runtime.

First let us create a simple POJO to hold employee records.

package project1;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement

public class Emp {
    public Emp() {
        super();
    }
    
    private String name;
    private int id;
    private int exp;
    private int salary;

    public Emp(String name, int id, int exp, int salary) {
        super();
        this.name = name;
        this.id = id;
        this.exp = exp;
        this.salary = salary;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setExp(int exp) {
        this.exp = exp;
    }

    public int getExp() {
        return exp;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getSalary() {
        return salary;
    }
    
    public String toString() {
        String empXml = "<name>"+getName()+"</name>"+"\n"+
                       "<id>"+getId()+"</id>"+"\n"+
                        "<experience>"+getExp()+"</experience>"+"\n"+
                       "<salary>"+getSalary()+"</salary>";
        return empXml;                  
    }
}
Then, create a REST service using the for the Employee.

package project1;

import javax.ws.rs.Path;
import java.util.*;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;

import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

@Path("/test")
public class Service {
    
   static ArrayList<Emp> emp = new ArrayList<Emp>();
  
    public Service() {
        super();
     }
    
    @GET
    @Produces("application/xml")
    public  ArrayList<Emp> getEmps() {
       
        return emp;
        
    }
    
    @DELETE
    public void deleteEmp(@QueryParam("id") int id) {
       emp.remove(getObj(id));
    }
    
    @PUT
    @Consumes("application/xml")
    public void addEmp( Emp e) {
        emp.add(e);
    }
    
    @PUT
    @Path("defaultEmp")
    public Response addEmp() {
        emp.add(new Emp("abc",1,5,10000));
        emp.add(new Emp("xyz",2,7,15000));
        emp.add(new Emp("lmn",3,5,8000));
        return Response.ok().build();
            
    }
    
    @POST
    public void upadteEmp(@QueryParam("id")int id,Emp e) {
      deleteEmp(id);
      addEmp(e);
    }
    
    public Emp getObj(int id) {
        Iterator i = emp.iterator();
        while(i.hasNext()){
        Emp emp = (Emp)i.next();
        if((emp.getId())==id)
        {
            System.out.println(emp.getName());
            return emp;
        }
        } 
        return null;
    }
}

We'll come to the UI part now.

After creating a Fusion Web Application from JDeveloper, create a new URL Data Control for the REST service created above (for GET and PUT Operations).

DataControls.dcx looks like below

DataControls.jpg



Now, our aim is to have a UI, from where we can enter the employee details. Once after having the data, construct parameter object and execute loadData (PUT) method.

This is done by having 4 input texts and bind them to attributes in the backing bean. Drag and Drop the loadData method from RestPut DataControl (and do not specify a value for the parameter).



And the code snippet of the jspx page

<af:panelFormLayout id="pfl1">
    <f:facet name="footer">
        <af:commandButton text="Put"
          disabled="#{!bindings.loadData.enabled}" id="cb1"
          actionListener="#{pageFlowScope.RestBean.performPut}"/>
    </f:facet>
    <af:inputText label="Id" id="it1" 
                  value="#{pageFlowScope.RestBean.id}" autoSubmit="true"/>
    <af:inputText label="Name" id="it2" 
                  autoSubmit="true" value="#{pageFlowScope.RestBean.name}"/>
    <af:inputText label="Exp" id="it3" 
                  value="#{pageFlowScope.RestBean.exp}" autoSubmit="true"/>
    <af:inputText label="Sal" id="it4" 
                  autoSubmit="true" value="#{pageFlowScope.RestBean.sal}"/>
</af:panelFormLayout>

In the backing bean (RestBean), we have 4 attributes with accessors that are mapped to the Text Items.

    private Number id,sal,exp;
    private String name;


    public void setId(Number id) {
        this.id = id;
    }

    public Number getId() {
        return id;
    }

    public void setSal(Number sal) {
        this.sal = sal;
    }

    public Number getSal() {
        return sal;
    }

    public void setExp(Number exp) {
        this.exp = exp;
    }

    public Number getExp() {
        return exp;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }


Now, we'll add an actionListener code in the backing bean, in that, we can construct a Map with the required values and then execute the method by passing Map to it.

    public void performPut(ActionEvent actionEvent) {
        BindingContainer bindings = getBindings();
        Map putParamMap = new HashMap();
        putParamMap.put("id", getId());
        putParamMap.put("name", getName());
        putParamMap.put("exp", getExp());
        putParamMap.put("sal", getSal());

        OperationBinding operationBinding = bindings.getOperationBinding("loadData");
        operationBinding.getParamsMap().put("emp",putParamMap);
        Object result = operationBinding.execute();
        if (!operationBinding.getErrors().isEmpty()) {
            System.out.println("Error processing put operation..");    
        }
    }

    public BindingContainer getBindings() {
        return BindingContext.getCurrent().getCurrentBindingsEntry();
    }
In the above code, we find the loadData method from the DataBindings entry, create a Map with all the required attributes to create an Employee record, get the Parameter list for the method and pass the Map to method as parameter to execute it.


Friday Mar 02, 2012

Refreshing One Column based on the value of Another Column in ADFdi Table

When using ADF Desktop Integration, quite frequently, we get into a situation where we would like to refresh one column based on the value of another column. In ADF Faces, we can achieve this by setting the autoSubmit property and partialTriggers property for the corresponding columns.

However, in ADFdi, we do not have such option. Though we can achieve this by using LOVs and Dependent LOVs. But, in some scenarios we would like to achieve this when using an Input Text Component.

In this article, we will simulate this Auto Refresh functionality in a ADFdi Table.

Note : Since we would be using VBA code to achieve this, we can use this only on the Macro Enabled Excel Workbooks.

Let us assume that we have a View Object based on the Emp table. We could take an example of having a transient attribute in the VO, that gives the sum of Salary and Commission attributes.

SalPlusComm.jpg



In the above example, we've added a new transient attribute (SalPlusComm) to the EmpVO, that would give the sum of Sal and Comm attributes. Since we need this attribute to get refreshed when either Sal or Comm attribute changes, we set the Sal and Comm attributes as Dependencies. Also, we set the AutoSubmit property (under UI Hints tab) for the Sal and Comm attributes.

SalCommAutoSubmit.jpg


Now, we are done with the model layer. We can now, create a jspx page and then Drag and Drop EmpView as ADF Table. After this, we create an Excel Workbook (macro enabled), enable ADF Desktop Integration for it, set the required Workbook Properties, and then add a Table based on the EmpView.

EmpTable.jpg



As there are no straight forward way in ADFdi to trigger a request to server when a value of a cell is changed, we will now add a DoubleClickActionSet for the Sal and Comm columns. This DoubleClickActionSet will have the Table.RowUpSync and Table.RowDownSync actions.

RowUpSyncDownSync.jpg


Above example image shows the DoubleClickActionSet for Sal column. In the same manner, we need to add the DoubleClickActionSet for the Comm column as well.

Now, we have the workbook, that would fetch the SalPlusComm attribute (after recalculation in the model), when we change the Sal / Comm attribute and then double click on that column. To do this automatically when the user tabs out / presses enter key on the cells, we'll write a bit of VBA Code on the Worksheet where we've this table (Go to Developer Tab and Click on Visual Basic).

Private Sub Worksheet_Change(ByVal Target As Range)
  If Target.Column = 14 Or Target.Column = 15 Then
    Target.Select
    Application.CommandBars("Cell").Controls("Invoke Action...").Execute
  End If
End Sub
Excel would trigger Worksheet_Change event when a cell in the worksheet is modified. So, we would code our logic in that event. The above code assumes that Sal column is present in the N (14th) column on the worksheet and Comm column is present in O (15th) column. So, we would execute our logic only when the contents in these two columns change.

ADFdi would provide a context menu (Invoke Action...) when a DoubleClickActionSet is added to a particular column. We'll make use of that context menu and invoke it programatically.


InvokeAction.jpg



We invoke that context menu programatically using the following line of code

Application.CommandBars("Cell").Controls("Invoke Action...").Execute

Now, we run our workbook, modify the value of Sal column for any row and tab out of that field would automatically update the value of SalPlusComm column.


EmpTable1.jpg
EmpTable1.jpg

Here, a simple example (Transient Attribute) is taken for the explanation. In the similar fashion, we can also have a DoubleClickActionSet to contain a method in the Impl that would perform this calculation as well.

Tip: If you are not able to view the image fully, right click on the image and choose View Image option to see it completely.


About

Tips & Tricks from Arun on JDev ADF, Forms, SQL & PL/SQL.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today