When you work on trees and need some fancy stuff on the UI, drag and drop feature in ADF is really cool. If you have the kind of requirements like add from one table/tree to other, copy from one table/tree to other, you can go for this.
In this post, I ll explain a basic example of implementing drag and drop in trees. The usecase is simple, where in the user wants to move an employee from one department to another department.
1. Have two EO's for Employees and Departments, create VO's based on them. Construct two trees on the UI side by side based on the VO's.
Let's consider the left tree as Source and the right one as Destination.
2. So, here to implement drag and drop, We need af:dragSource and af:collectionDropTarget.
Add af:dragSource to the left af:tree and af:collectionDrop target to the right af:tree like below.
3. The properties of af:dragSource are like below
Action is MOVE here because, we are moving from one department to another.
We can used COPY when there is some requirement like, adding employees to favourites section etc, where you don't delete from the source. Use of Discriminant property will be shown in the next step.
4. The properties of af:collectionDropTarget are like below
The Discriminant in the dragSource and ModelName in the collectionDropTarget should be same.
We need a dropListener to write the logic to update the department id of the dragged employee.
5. Going into implementation, we can add some validations like below:
Here the important points to understand is, we need to get the dragComponent binding and the dropComponent binding (in this case RichTree) from the dropEvent.
DataFlavour is RowKeySet, as we are working with rows, in af:collectionDropTarget, RowKeySet DataFlavour is assumed by default. But, If u you use af:dropTarget, it specifcally asks for the DataFlavour.
Common errors, you would get are like tree might not be refreshing, after the drag and drop tree is not in stable state etc. That would be because, drag and drop disturbs the state of the tree. So, as shown in the above code, before starting with the logic, get the row keys of both the drag and drop trees and set them back at the end of the code.
This way, you can give your customer/ end user good fancy features in the ADF application.
In this post, I ll explain a basic example of implementing drag and drop in trees. The usecase is simple, where in the user wants to move an employee from one department to another department.
1. Have two EO's for Employees and Departments, create VO's based on them. Construct two trees on the UI side by side based on the VO's.
Let's consider the left tree as Source and the right one as Destination.
2. So, here to implement drag and drop, We need af:dragSource and af:collectionDropTarget.
Add af:dragSource to the left af:tree and af:collectionDrop target to the right af:tree like below.
3. The properties of af:dragSource are like below
Action is MOVE here because, we are moving from one department to another.
We can used COPY when there is some requirement like, adding employees to favourites section etc, where you don't delete from the source. Use of Discriminant property will be shown in the next step.
4. The properties of af:collectionDropTarget are like below
The Discriminant in the dragSource and ModelName in the collectionDropTarget should be same.
We need a dropListener to write the logic to update the department id of the dragged employee.
5. Going into implementation, we can add some validations like below:
- Department should not be dragged.
- Should not be dropped on Employee
- Employee should not be dropped on to the same department.
Code for the dropListener looks like below.
public DnDAction dragDropListener(DropEvent dropEvent) {
RichTree dragTree = (RichTree)dropEvent.getDragComponent();
RichTree dropTree = (RichTree)dropEvent.getDropComponent();
String dragNodeVO = null;
Transferable t = dropEvent.getTransferable();
DataFlavor<RowKeySet> df =
DataFlavor.getDataFlavor(RowKeySet.class, "rowMove");
RowKeySet rks = t.getData(df);
Iterator iter = rks.iterator();
Object dragCurrentRowKey = dragTree.getRowKey();
Row dragRow = null;
if (iter.hasNext()) {
List key = (List)iter.next();
dragTree.setRowKey(key);
JUCtrlHierNodeBinding rowBinding =
(JUCtrlHierNodeBinding)dragTree.getRowData();
dragRow = rowBinding.getRow();
dragNodeVO = dragRow.getStructureDef().getDefName();
if (!("EmployeesView".equalsIgnoreCase(dragNodeVO))) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Drag and Drop an employee to move from department to department.");
FacesContext.getCurrentInstance().addMessage(null, msg);
return DnDAction.NONE;
}
}
Object currentRowKey = dropTree.getRowKey();
List dropRowKey = (List)dropEvent.getDropSite();
if (dropRowKey == null) {
return DnDAction.NONE;
}
dropTree.setRowKey(dropRowKey);
JUCtrlHierNodeBinding dropNode =
(JUCtrlHierNodeBinding)dropTree.getRowData();
Row dropRow = dropNode.getRow();
String dropNodeVO = dropRow.getStructureDef().getDefName();
if ("DepartmentsView".equalsIgnoreCase(dropNodeVO)) {
Long deptId = (Long)dropRow.getAttribute("DepartmentId");
Long oldDeptId = (Long)dragRow.getAttribute("DepartmentId");
if (oldDeptId != deptId) {
dragRow.setAttribute("DepartmentId", deptId);
executeOperation("Commit");
} else {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Drop an employee on to a different department, he is already from this department.");
FacesContext.getCurrentInstance().addMessage(null, msg);
return DnDAction.NONE;
}
} else {
FacesMessage msg =
new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Drop an employee on to a department to move from department to department.");
FacesContext.getCurrentInstance().addMessage(null, msg);
return DnDAction.NONE;
}
dropTree.setRowKey(currentRowKey);
dragTree.setRowKey(dragCurrentRowKey);
AdfFacesContext.getCurrentInstance().addPartialTarget(dragTree);
AdfFacesContext.getCurrentInstance().addPartialTarget(dropTree);
return DnDAction.MOVE;
}
Here the important points to understand is, we need to get the dragComponent binding and the dropComponent binding (in this case RichTree) from the dropEvent.
DataFlavour is RowKeySet, as we are working with rows, in af:collectionDropTarget, RowKeySet DataFlavour is assumed by default. But, If u you use af:dropTarget, it specifcally asks for the DataFlavour.
Common errors, you would get are like tree might not be refreshing, after the drag and drop tree is not in stable state etc. That would be because, drag and drop disturbs the state of the tree. So, as shown in the above code, before starting with the logic, get the row keys of both the drag and drop trees and set them back at the end of the code.
This way, you can give your customer/ end user good fancy features in the ADF application.
Hi Nitish,
ReplyDeleteGreat post, helped me a lot. Thank you so much for post.
Exactly I have same requirement, where need drag and drop between trees. I have followed you post and getting NULL to get dragTree data/row. At this line.
JUCtrlHierNodeBinding rowBinding = (JUCtrlHierNodeBinding)dragTree.getRowData();
In my requirement trees are all together on different View Objects. One tree will show all persons from PERSONS table and another tree is showing Departments and Employees.
So here requirement is, by drag Person from Persons tree and dropping on Dept Name should create new employee with person details.
Can we do drag and drop between tree, which is created on different View Objects ?
Any idea, why I am getting NULL while getting JUCtrlHierNodeBinding rowBinding =
(JUCtrlHierNodeBinding)dragTree.getRowData();
Please help me.
Thank you,
Srikanth
Hi SrikS,
DeleteUr requirement is definetely achievable ... It all depends on the logic u write on the listener..
Coming to the null pointer exception, did u set selection single on ur tree??
Thanks
Nitish