Please disable your adblock and script blockers to view this page

Search this blog

Wednesday, 1 October 2014

Using Drag-Drop functionality in af:treeTable to move data between nodes -Oracle ADF

This is a very common question and development requirement - How to drag drop records inside treeTable to perform some kind of operation ?
This post is an answer,here i am using Departments and Employees table of HR Schema to create treeTable and Jdeveloper 12.1.3

First Step-
Create business components using Departments and Employees table of HR Schema and create association and viewLink using DepartmentId (here Departments is parent and Employee is child viewObject)




now drop Departments viewObject as treeTable on page from data-control
see- Tree Table Component in Oracle ADF(Hierarchical Representation)  (How to create treeTable in ADF)

So after creating simple treeTable, added  two columns other than nodeStamp to show DepartmentName and EmployeeName in a better way
see-Tree Table Component with declarative presentation in ADF

Finally it looks like this-


Now my requirement is - when drag an Employee to any Department node it should become child of that Department ,means attach Employee to Departments using drag-drop

So to drag Employee Name filed i added af:attributeDragSource under this field and to get corresponding EmployeeId added an af:clientAttribute component




This clientAttribute stores value of EmployeeId for each node and dragSource refers clientAttribute to identify node value
Now to handle dropEvent on DepatmentName node , dropped an af:dropTarget under this field and created a dropListener in managed bean that will handle desired operation on dropEvent


See complete af:treeTable xml source-

<af:treeTable value="#{bindings.Departments1.treeModel}" var="node"
                  selectionListener="#{bindings.Departments1.treeModel.makeCurrent}" rowSelection="single" id="tt1"
                  binding="#{pageFlowScope.DragDropBean.treeTabBind}">
      <f:facet name="nodeStamp">
        <af:column id="c1" width="10">
          <af:outputText value="#{node}" id="ot1"/>
        </af:column>
      </f:facet>
      <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot2"/>
      </f:facet>
      <af:column id="c2" width="150">
        <af:outputText value="#{node.DepartmentName}" id="ot3"
                       inlineStyle="font-weight:bold;color:red;font-size:small;">
          <af:dropTarget dropListener="#{pageFlowScope.DragDropBean.treeDropListener}">
            <af:dataFlavor flavorClass="java.lang.Object"/>
          </af:dropTarget>
        </af:outputText>
      </af:column>
      <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot4" inlineStyle="font-weight:bold;color:darkgreen;">
          <af:clientAttribute name="DragEmployeeId" value="#{node.EmployeeId}"/>
          <af:attributeDragSource attribute="DragEmployeeId"/>
        </af:outputText>
      </af:column>
    </af:treeTable>

Now see the code of drop listener written in managed bean that gets EmployeeId value from dropEvent and DepartmentId from expression and calls a business logic to move employee under that Deaprtment

DropListener in ManagedBean to handle dropEvent-

    /**Method to move Employee Under Department
     * @param dropEvent
     * @return
     */
    public DnDAction treeDropListener(DropEvent dropEvent) {

        Object empId = null;
        Integer dropVal = null;
        //Get Value from dragSource (EmployeeId of dragged node)
        DataFlavor<Object> df = DataFlavor.getDataFlavor(Object.class);
        empId = dropEvent.getTransferable().getData(df);
        if (empId != null) {
            System.out.println("Dropped value is -" + empId);
            try {
                dropVal = new Number(empId).intValue();
            } catch (SQLException e) {
            }
            //Get DepartmentId where Employee is dropped (resolve Expression langusge)
            Object droppedUnderDept = resolvElDC("#{node.DepartmentId}");
            System.out.println("Dropped under-" + droppedUnderDept);
            //Method Call to mode Employee under Department
            OperationBinding ob = executeOperation("moveEmployeeToDepartment");
            ob.getParamsMap().put("deptId", Integer.parseInt(droppedUnderDept.toString()));
            ob.getParamsMap().put("empId", dropVal);
            ob.execute();
        }
        /*Method to expand treeTable to show all nodes
         * see-http://www.awasthiashish.com/2013/10/expand-and-collapse-aftreetable.html
         * */
        expandTreeTable();
        //Refreshing treeTable after all operation, here treeTabBind is component binding of treeTable
        AdfFacesContext.getCurrentInstance().addPartialTarget(treeTabBind);
        return DnDAction.NONE;

    }

Helper methods to get BindingContainer and resolve expression-

    /*****Generic Method to call operation binding**/
    public BindingContainer getBindingsCont() {
        return BindingContext.getCurrent().getCurrentBindingsEntry();
    }

    /**
     * Generic Method to execute operation Binding
     * */
    public OperationBinding executeOperation(String operation) {
        OperationBinding createParam = getBindingsCont().getOperationBinding(operation);
        return createParam;

    }



    /**Method to resolve Expression
     * @param data
     * @return
     */
    public Object resolvElDC(String data) {
        FacesContext fc = FacesContext.getCurrentInstance();
        Application app = fc.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = fc.getELContext();
        ValueExpression valueExp = elFactory.createValueExpression(elContext, data, Object.class);
        return valueExp.getValue(elContext);
    }

Now run your application and check -
Dragged Prabhakar from Purchase Department and dropped it on Finance-

 Result-
Thanks, Happy learning :)
Download Sample App
See other post on Drag-Drop functionality- Adding Drag and Drop Functionality for collections in page fragments to create insert

Monday, 22 September 2014

Invoke button action (ActionEvent) and value change listener using JavaScript- Oracle ADF

This is another post about using JavaScript in ADF Faces, So again nothing complicated just simple things but needed often
In this post i am covering two simple requirements , using Jdeveloper 11.1.2.4 (11g Release2)

1. How to call button action (ActionEvent) using JavaScript
2. How to call valueChangeListener of a component using JavaScript

 

Call button action using JavaScript-


So for this just drop a commandButton and an inputText on page and create a ActionListener in managed bean for button



Code in managed bean for ActionEvent-




    /**Action Listener for Button
     * @param actionEvent
     */
    public void buttonAction(ActionEvent actionEvent) {
        FacesMessage msg = new FacesMessage("Button Action called");
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, msg);
    }

Now requirement is to call this button action on double click event of mouse in inputText
To handle ActionEvent in JavaScript, framework provides a class AdfActionEvent , it has a method to queue ActionEvent from a component. So here i use same method to call ActionEvent

See JavaScript function-

function callButtonAction() {
                  //Method to get component using id (here button is top container)
                  var button = AdfPage.PAGE.findComponentByAbsoluteId('cb1');
                  //Method to queue ActionEvent from component
                  AdfActionEvent.queue(button, button.getPartialSubmit());
              }

and call it using af:clientListener under af:inputText-



Run page and check on double click of mouse in inputText-




Call ValueChangeListener using JavaScript-


This is also same as pervious one, created a valueChangeListener in managed bean for input text and requirement is to call this listener on mouse hover of button

Code in managed bean for ValueChangeListener-

    /**ValueChangeListener for inputText
     * @param valueChangeEvent
     */
    public void inputTextVCE(ValueChangeEvent valueChangeEvent) {
        FacesMessage msg = new FacesMessage("Value Change Lestener called");
        FacesContext context = FacesContext.getCurrentInstance();
        context.addMessage(null, msg);
    }

There is a class to handle valueChange event using JavaScript - AdfValueChangeEvent
So this class also has a queue method , the difference is this queue method takes 4 parameters and also requires a value change event on component
So first step is to change (set) value of field using JavaScript and then queue valueChangeEvent

See JavaScript function-


 function callValueChangeEvent(evt) {
                  //Method to get component using id (here inputText is top container)
                  var field = AdfPage.PAGE.findComponentByAbsoluteId('it1');
                  //Change(set) field's value
                  field.setValue('I am JavaScript text');
                  //Get New changed value
                  var newVal = field.getValue();
                  //Queue ValueChangeEvent (component,oldValue,newValue,autoSubmit)
                  AdfValueChangeEvent.queue(field, null, newVal, false);

              }

make sure that clientComponent and autoSubmit property of inputText set to true
call this javascript fucntion from button using af:clientListener


Run page and check it-
Thanks, Happy Learning


Monday, 15 September 2014

Showing all values instead of 'All' text in selectManyChoice using JavaScript -Oracle ADF

Happy Engineer's Day :)
This post is about overriding default text for select all feature in af:selectManyChoice
af:selectManyChoice supports multiple selection in ADF Faces and framework provides a default feature to select all values of list but after selecting all value it doesn't show values ,only show a String 'All'.
Like this-
but sometimes it is not clear that what are the values by just seeing this 'All' text or it is a requirement to show all values (if there is not much data) instead of this default text



So in this post i am going to override this default text using JavaScript and this is a quick overview that how we can play with JavaScript and try it in ADF Faces
see previous posts on JavaScript in Oracle ADF-

Show message (Invoke FacesMessage) using JavaScript in ADF Faces
Launching browser print dialog using simple javascript function in ADF
Using JQuery in Oracle ADF

So in this implementation i am using Departments table (HR Schema -Oracle) and Jdeveloper 12C (12.1.3)
  • Prepare model using Departments table and drop viewObject on page as af:selectManyChoice


  • Now  see when we select one -two values , it appears on component but in case of all only that 'All' string appears


  • Here i am changing this text on valueChange event of selectManyChoice , so created a valueChangeListener in managed bean. 

  • See what i am going to do in this listener is -
    1. Get all selected values means DepartmentId
    2. Next is to get corresponding DepartmentName for DepartmentId 
    3. Add all DepartmentName into an String
    4. Call JavaScript method to set this String as value of selectManyChoice instead of 'All'

  • See code written in valueChangeListener , i have used enough comments to understand each line

  •     /**ValueChangeListener for SelectManyChoice (Executes Javascript to replace 'All' text)
         * @param vce
         */
        public void selectManyVCE(ValueChangeEvent vce) {
            //String to store all selected Departments Name
            String displayVal = "";
            //Get BindingContainer of current page
            BindingContext bctx = BindingContext.getCurrent();
            BindingContainer bindings = bctx.getCurrentBindingsEntry();
            //Get Iterator of SelectManyChoice
            DCIteratorBinding iter = (DCIteratorBinding) bindings.get("Departments1Iterator");
    
            if (vce.getNewValue() != null) {
                //Get all selected values in an Object array
                Object[] selectedVals = (Object[]) vce.getNewValue();
                //Iterate over array to get all selected DepartmentId
                for (int i = 0; i < selectedVals.length; i++) {
                    Integer val = (Integer) selectedVals[i];
                    //Create Key using DepartmentId to use furhter
                    Key key = new Key(new Object[] { val });
                    //Get ViewObject row using Key vlaue
                    Row row = iter.getViewObject().getRow(key);
                    // Get DepartmentName from row and add it to String
                    if (displayVal != "") {
                        displayVal = displayVal.concat(", ").concat(row.getAttribute("DepartmentName").toString());
                    } else {
                        displayVal = displayVal.concat(row.getAttribute("DepartmentName").toString());
                    }
                }
                //Write JavaScript code to change text of selectManyChoice as a StingBuilder Object
                StringBuilder jsString = new StringBuilder();
                //First Step-get clientID of component
                jsString.append("var elementId = '" + vce.getComponent().getClientId());
                //Second- add ::content to access it's value
                jsString.append("::content';");
                //Third- Check that current value is 'All' or not
                jsString.append("\n if (document.getElementById(elementId).value == 'All') {");
                //Forth- if yes then assign Department Name's string as value of selectManyChoice
                jsString.append("\n document.getElementById(elementId).value ='" + displayVal + "' \n};");
                System.out.println("JS File-" + jsString);
                //Call this JavaScript code using this Helper method
                writeJavaScriptToClient(jsString.toString());
    
            }
        }
    

    Method to call JavaScript from managed bean-

        /**Helper Method to call Javascript
         * @param javascriptCode
         */
        public static void writeJavaScriptToClient(String javascriptCode) {
            FacesContext facesCtx = FacesContext.getCurrentInstance();
            ExtendedRenderKitService service = Service.getRenderKitService(facesCtx, ExtendedRenderKitService.class);
            service.addScript(facesCtx, javascriptCode);
        }
    

    Import packages-

    import javax.faces.context.FacesContext;
    import javax.faces.event.ValueChangeEvent;
    
    import oracle.adf.model.BindingContext;
    import oracle.adf.model.binding.DCIteratorBinding;
    
    import oracle.binding.BindingContainer;
    
    import oracle.jbo.Key;
    import oracle.jbo.Row;
    
    import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
    import org.apache.myfaces.trinidad.util.Service;
    

  • Run application and check, on selecting all values it shows -


  • but this will happen only on value change event of selectManyChoice , you can call this JavaScript any time as per your requirement like on page load
Thanks , Happy Learning :-)