Dropping trees

There are many ways to reassign employees. You, as a programmer or DBA could, for instance, write an update statement to do that. But let’s say that you don’t want to lose your friends at the HR department. They want you to build a real flashy GUI to achieve the very same functionality. Ooops. No more lunch breaks for a week or two, because you planned this task to take only half an hour (the time that writing an update statement takes, if you type very slowly and test it intensively). ADF 11g provides you with two very handy features. First there’s the tree which is not new, but enhanced. Second there’s drag and drop functionality. Here is a way to use those two into a powerful combination.

The initial steps are as usual:
•    create a new JDeveloper application
•    in the Model project, create Business Components from Tables for table EMPLOYEES and DEPARTMENTS (HR schema)
•    in the View project, create a new JSF page;
Now you create a tree. This is a lot more straightforward then it was in any of the previous releases of ADF. You just drop the DepartmentView1 from the datacontrol on the page as a tree. The treebinding editor opens and from there you can add all of the child collections as child nodes.

 

Dropping trees treebindingeditor

 

That is all that it takes to create a fully functional tree. Run the page. Just expand and collapse some nodes to test if the tree is working correctly.

Dropping trees theworkingtree

Let’s add the drag and drop functionality. We need to add a collectionDragSource to the tree.

<af:tree id="tree" value="#{bindings.DepartmentsView1.treeModel}" 
                   var="node"
                    selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
                    rowSelection="single"  inlineStyle="height:800px;">
            <af:collectionDragSource actions="MOVE" 
                                     modelName="employee"/>
            <f:facet name="nodeStamp">
              <af:outputText value="#{node}"/>
            </f:facet>
</af:tree> 

Because we want to use the same tree as droptarget, we need to add a collectionDropTarget as well. Do that right after the collectionDragSource, but before the <f:facet>.

<af:collectionDropTarget actions="MOVE" 
                         modelName="employee"
                         dropListener="#{reAssign.onTreeDrop}"/>

You will notice the dropListener. This is a callback to a method that can handle the drag and drop action. You can create this method yourself, or just copy the code and configure the bean in the config.xml.

  <managed-bean>
    <managed-bean-name>reAssign</managed-bean-name>
    <managed-bean-class>
         nl.amis.empdept.view.beans.reAssign
     </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

When you are dragging and dropping, the actual goal is to reassign the dragged employee to the department where you dropped it on. Therefore you need more information than just the employee data that you dragged and dropped. You also need the data were you dropped upon.

So what did you actually drop? In the onTreeDrop method in the reAssign bean you can find the droppedValue by invoking the getData method on the dropEvent.

DataFlavor<RowKeySet> df = DataFlavor.getDataFlavor(RowKeySet.class);
RowKeySet droppedValue = dropEvent.getTransferable().getData(df);  
Object[] keys = droppedValue.toArray();
Key empKey = null;
// get the employee which we want to update the department
for (int i = 0; i < keys.length; i++) {
      List list = (List)keys[i];
      empKey = (Key)list.get(1);


The object Array keys contains all the dropped rowkeys. The first position of the list contains the departmentId of the dropped employee, and the second entry contains the employeeId. So now you know which employee you dropped.

Let’s find out where you dropped the employee on? The dropEvent class has a getDropSite method which will provide you with the necessary information. This method can return a null value indicating that the drop occurred outside the data portion. If the result is null you will do nothing. Because in this case you know that you are dropping on a tree, you can cast the getDropComponent to a RichTree, and from thereon continue processing.

Object serverRowKey = dropEvent.getDropSite();
if (serverRowKey != null) {
    RichTree richTree = (RichTree)dropEvent.getDropComponent();
    richTree.setRowKey(serverRowKey);
    int rowIndex = richTree.getRowIndex();
    Number departmentNew =                
        (Number)((JUCtrlHierNodeBinding)richTree.getRowData(rowIndex)).getAttribute("DepartmentId");
    }

Now you have the Id of the department you dropped in, and also the employee that was dragged. So far, you can actually only drop ON a department node, not IN it. That is because the DepartmentId is only available in the department node. So if you drop ON an employee you have to go 1 level up to find the DepartmentId. If you add the following, then it’s also possible to drop IN a department node:

Number departmentNew = null;
if (richTree.getDepth() == 0) {
       departmentNew =  
                 (Number)((JUCtrlHierNodeBinding)richTree.getRowData(rowIndex)).getAttribute("DepartmentId");
     } else {
       departmentNew = 
                 (Number)((JUCtrlHierNodeBinding)richTree.getRowData(rowIndex)).getParent().getAttribute("DepartmentId");
     }

The only thing left to do is update the employee with the new departmentId.

Get a handle to the applicationModule, find the employee that needs to be updated, set the new departmentId, commit, clear the cache of the viewobject and requery it.

 

   EmpDeptServiceImpl am = getAm();   
   EmployeesViewRowImpl empRow =                             
      (EmployeesViewRowImpl)am.getEmployeesView1().getRow(empKey);
   empRow.setDepartmentId(departmentNew);
   am.getDBTransaction().commit();
   // requery the employees view
   am.getDepartmentsView1().clearCache();
   am.getDepartmentsView1().executeQuery();

That’s all. It’s not necessary to refresh the tree by invoking addPartialTarget(), because the target component is automatically redrawn in response to a successful drop.

Now you have a working tree with drag and drop functionality.
When you drag an employee from one to another department…

Dropping trees duringthedrag

…this employee is reassigned! Cool isn’t it?

Dropping trees afterthedrop

It even gets better. In the reassign method the droppedValue is already put into an array (the keys array), so it is possible to iterate all these values.
This means that you can handle multiply employees in one go and even then drag and drop works!
The only thing you have to change in your page is the rowSelection attribute of the tree element. This attribute can have three values: none, single and multiple.

So if you change it into multiple you should be able to select multiple employees and reassign them in one single drop action.
And on top of that, you can reassign employees from different departments to one department in one action.

You can do this by using the control-button and skip the department root nodes.

Select some employees…

Dropping trees duringmultipledrag

…and drop them on a department.

Dropping trees aftermultipledrop

They are all reassigned.

 

Yes! Now the HR department will throw you a party!

 

 

 

You can download the example workspace here.

10 Comments

  1. Luc Bors January 14, 2010
  2. Rohit January 4, 2010
  3. J Developer October 29, 2009
  4. J Developer October 28, 2009
  5. Dom June 12, 2009
  6. Ansar August 20, 2008
  7. Luc Bors July 14, 2008
  8. Frank Nimphius July 14, 2008
  9. Lucas Jellema July 11, 2008
  10. Edwin Biemond July 11, 2008