With a little effort I adjusted some of my code to handle drag and drop capabilities of both select boxes and data grids on the same form.
I reuse the event listener functions for each of the objects. Press the refresh data button to load the grids and drag away!!!
Live Example
***For those having issues with copying and pasting the code here is the download:***
Code
***New Code added that checks for uniqueness in the destination grid and select based off of a key field (hope this helps Brian):***
Code
Live Example
The Code:
<cfform name="slushForm" format="Flash" onload="formOnLoad()" width="800" timeout="500">
<cfformitem type="script">
//Drag Drop enable for grids
var overSourceItem:Boolean = false;
var overTargetItem:Boolean = false;
//Reference the grids
var gridSource:Object = {};
var gridDestination:Object = {};
function addData()
{
//Add some data to the source grid
grid1.removeAll();
grid2.removeAll();
select1.removeAll();
select2.removeAll();
grid1.addItem({lastName:"Smith",firstName:"Jane",phone:"(555) 555-1234"});
grid1.addItem({lastName:"Jones",firstName:"Jen",phone:"(555) 555-5678"});
select1.addItem({data:1, label:"Smith, Jane"});
select1.addItem({data:1, label:"Jones, Jen"});
}
function formOnLoad()
{
grid1.dragEnabled=true;
grid1.multipleSelection=true;
grid1.addEventListener('dragEnter', doDragEnter);
grid1.addEventListener('dragOver', doDragOverSource);
grid1.addEventListener('dragExit', doDragExitSource);
grid1.addEventListener('dragDrop', doDragDropDGSource);
grid1.addEventListener('dragComplete', doDragCompleteDGSource);
grid2.dragEnabled=true;
grid2.multipleSelection=true;
grid2.addEventListener('dragEnter', doDragEnter);
grid2.addEventListener('dragOver', doDragOverDest);
grid2.addEventListener('dragExit', doDragExitDest);
grid2.addEventListener('dragDrop', doDragDropDGDest);
grid2.addEventListener('dragComplete', doDragCompleteDGDest);
select1.dragEnabled=true;
select1.multipleSelection=true;
select1.addEventListener('dragEnter', doDragEnter);
select1.addEventListener('dragOver', doDragOverSource);
select1.addEventListener('dragExit', doDragExitSource);
select1.addEventListener('dragDrop', doDragDropDGSource);
select1.addEventListener('dragComplete', doDragCompleteDGSource);
select2.dragEnabled=true;
select2.multipleSelection=true;
select2.addEventListener('dragEnter', doDragEnter);
select2.addEventListener('dragOver', doDragOverDest);
select2.addEventListener('dragExit', doDragExitDest);
select2.addEventListener('dragDrop', doDragDropDGDest);
select2.addEventListener('dragComplete', doDragCompleteDGDest);
}
function doDragEnter(event) {
event.handled = true;
}
function doDragExitSource(event) {
_root.overSourceItem = false;
event.target.hideDropFeedback();
}
function doDragExitDest(event) {
_root.overTargetItem = false;
event.target.hideDropFeedback();
}
function doDragOverSource(event) {
_root.gridSource = event.target;
if (_root.gridDestination.dataProvider.length > 0)
{
_root.overSourceItem = true;
event.target.showDropFeedback();
if (Key.isDown(Key.CONTROL))
event.action = mx.managers.DragManager.COPY;
else if (Key.isDown(Key.SHIFT))
event.action = mx.managers.DragManager.LINK;
else
event.action = mx.managers.DragManager.MOVE;
}
}
function doDragOverDest(event) {
_root.gridDestination = event.target;
if (_root.gridSource.dataProvider.length > 0)
{
_root.overTargetItem = true;
event.target.showDropFeedback();
if (Key.isDown(Key.CONTROL))
event.action = mx.managers.DragManager.COPY;
else if (Key.isDown(Key.SHIFT))
event.action = mx.managers.DragManager.LINK;
else
event.action = mx.managers.DragManager.MOVE;
}
}
function doDragDropDGSource(event) {
doDragExitDest(event);
var dragItems = event.dragSource.dataForFormat('items');
var dest = event.target;
var dropLoc = dest.getDropLocation();
var destParentNode = dest.getDropParent();
dest.addItemsAt(dropLoc,dragItems);
}
function doDragDropDGDest(event) {
doDragExitSource(event);
var dragItems = event.dragSource.dataForFormat('items');
var dest = event.target;
var dropLoc = dest.getDropLocation();
var destParentNode = dest.getDropParent();
dest.addItemsAt(dropLoc,dragItems);
}
function doDragCompleteDGSource(event){
var dest = event.target;
var dropLoc = dest.getDropLocation();
var destParentNode = dest.getDropParent();
var selectedIn:Array = event.target.selectedIndices;
//descending sort to remove items that have been dropped into target grid
selectedIn.sort(16|2);
if (_root.overTargetItem)
{
for(var i = 0; i < selectedIn.length; i++){
//add the selected items
_root.gridDestination.dataProvider.addItem(event.target.selectedItems[i]);
}
for(var i = 0; i < selectedIn.length; i++){
//remove this item
event.target.dataProvider.removeItemAt(selectedIn[i])
}
}
_root.overTargetItem = false;
_root.gridDestination.hideDropFeedback();
}
function doDragCompleteDGDest(event){
var dest = event.target;
var dropLoc = dest.getDropLocation();
var destParentNode = dest.getDropParent();
var selectedIn:Array = event.target.selectedIndices;
//descending sort to remove items that have been dropped into target grid
selectedIn.sort(16|2);
if (_root.overSourceItem)
{
for(var i = 0; i < selectedIn.length; i++){
//add the selected items
_root.gridSource.dataProvider.addItem(event.target.selectedItems[i]);
}
for(var i = 0; i < selectedIn.length; i++){
//remove this item
event.target.dataProvider.removeItemAt(selectedIn[i])
}
}
_root.overSourceItem = false;
_root.gridSource.hideDropFeedback();
}
</cfformitem>
<cfformgroup type="accordion">
<cfformgroup type="page" label="Slush Grid">
<cfformgroup type="horizontal">
<cfformgroup type="hbox">
<cfformitem type="text" height="15" style="font-weight:bold">Source Grid</cfformitem>
<cfgrid name="grid1" rowheaders="false" height="85">
<cfgridcolumn name="lastName" header="Last" bold="no">
<cfgridcolumn name="firstName" header="First" bold="no">
<cfgridcolumn name="phone" header="Phone" bold="no">
</cfgrid>
</cfformgroup>
<cfformgroup type="hbox">
<cfformitem type="text" height="15" style="font-weight:bold">Destination Grid</cfformitem>
<cfgrid name="grid2" rowheaders="false" height="85">
<cfgridcolumn name="lastName" header="Last" bold="no">
<cfgridcolumn name="firstName" header="First" bold="no">
<cfgridcolumn name="phone" header="Phone" bold="no">
</cfgrid>
</cfformgroup>
</cfformgroup>
</cfformgroup>
<cfformgroup type="page" label="Slush Select">
<cfformgroup type="horizontal">
<cfformgroup type="hbox">
<cfformitem type="text" height="15" style="font-weight:bold">Source Select</cfformitem>
<cfselect name="select1" size="3" multiple="yes" />
</cfformgroup>
<cfformgroup type="hbox">
<cfformitem type="text" height="15" style="font-weight:bold">Destination Select</cfformitem>
<cfselect name="select2" size="3" multiple="yes" />
</cfformgroup>
</cfformgroup>
</cfformgroup>
</cfformgroup>
<cfinput type="button" name="btnAddData" value="Refresh Data" onClick="addData();">
</cfform>
Namely: dragging to the right side grid makes the record vanish from both grids.
and pressing the refresh data button adds the 2 records in addition to what ever remains in the left side instead of replacing it.
did you post the same code that you use in your live example?
Thanks,
Chris
now it works great!
now it works great!
How could I use this code to re-order a simple select list?
thanks.
rick
I'm not clear on what exactly you are asking. When you say re-order do you mean sort?
--Jeff
thanks...
Your explain why don't work in my server?
I install updater 2 to Colfusion and it doesn't work....!
Please help me...!
I have the same problem that it:
"your example works fine for me using the live example link. but when i run the posted code on my server, it doesnt function properly.
Namely: dragging to the right side grid makes the record vanish from both grids.
and pressing the refresh data button adds the 2 records in addition to what ever remains in the left side instead of replacing it."
did you post the same code that you use in your live example?"
If so, what parts of the code would need to be changed? I've tried this in an example and I can drag the item, but it disappears when I drop it and doesn't remove itself from the list.
Thanks!
I just ask myself how one can achieve that awful onClick hiding of the hbox ?
How do you do that - Can you give me a hind?
Thx in Advance, Michael
Did you have this problem and/or do you have any examples of the code you use on the results page?
Thanks in advance.
Catherine
Thanks,
Jacob
Lovin the slush! I got it working correctly pushing data over as per your example. We have gone one step further and created an edit functionality with the second box. We found that when you fill the grid with data that it works okay, but when you submit it to the CFC the form is blank.
Below is the code that we are using.
We are doing a sort by on one of the columns as soon as we edit it, it works, however when you try and remove an item from the list it fails.
Have I gone to far with this code.
<cfgrid name="gridSource" rowheaders="false" height="85">
<cfgridcolumn name="PostCodeID" header="PostCodeID" bold="no">
<cfgridcolumn name="Postcode" header="Postcode" bold="no">
<cfloop query="getPostCodes">
<cfgridrow data ="#PostcodeID#,#Postcode#">
</cfloop>
</cfgrid>
<cfformitem type="text" height="15" style="font-weight:bold">Postcodes you want the Representative to relate to.</cfformitem>
<cfgrid name="gridDestination" rowheaders="false" height="85" selectmode="edit" >
<cfgridcolumn name="PostCodeID" header="PostCodeID" bold="no">
<cfgridcolumn name="Postcode" header="Postcode" bold="no">
<cfgridcolumn name="sortby" header="Order" bold="no" >
<cfgridcolumn name="pfid" header="pfid">
<cfloop query="getRepPostCodes">
<cfgridrow data="#PostCodeID#,#Postcode#,#sortby#,#pfid#">
</cfloop>
</cfgrid>
I do have a question, do you know how to make it so the source wil copy into the destinationGrid but not duplicate. I think It could be possible with a little if then statement, but Im not sure how to approach it.
I got rid of this bit of code:
for(var i = 0; i < selectedIn.length; i++){
//remove this item
event.target.dataProvider.removeItemAt(selectedIn[i]);
and that make it's copy without removing, but if you copy it again it will duplicate.
Have any ideas on how to prevent that?
Thanks.
There is two ways to accomplish that. Either check for the values already added when populating the source grid or on completion of your drag loop over an existing key field in the destination grid dataprovider to check if it is there. If the item is already added then make the drag fail returning the item to the source or removing entirely.
Always two approaches in this scenario, either don't let the user do something that will fail or let the user do something that does cause a failure. I tend to go with not allowing the user to do something that will fail in the system when possible unless there is a significant performance hit in doing so. If I have time I believe I implemented the behavior you are requesting. If I get time I'll submit another reply to the blog entry.
I added what I think you need above, you can view the demo and download the zip. To get the multiple (same value scenario working just click the refresh search button after your first drag out of the search lists). Let me know how it works for you.
Jeremy, sorry for the late reply... schedule has been tight. Can you send me your source?
<!---- for(var i = 0; i < selectedIn.length; i++){
//remove this item from Source
event.target.dataProvider.removeItemAt(selectedIn[i]);
} ---->
and
<!----for(var i = 0; i < selectedIn.length; i++){
//add the selected items to Source
_root.gridSource.addItem(event.target.selectedItems[i]);
}--->
I ended up getting a bug when you selected an item in the destination grid, and started to drag it, it would remove it on release, regardless of whether or not i was still over the destination grid.
SO I changed:
//descending sort to remove items that have been dropped into target grid
selectedIn.sort(16|2);
if (!overDestinationItem) {
to:
//descending sort to remove items that have been dropped into target grid
selectedIn.sort(16|2);
if (overSourceItem) {
It was this way in your original code, but had been changed in the latest zip.
I got you, yes, it was coded that way due to customer request. It was changed from the initial example. Sorry for any confusion there. The only time my example code would send it back to the orginating grid was if they were over said grid. Thanks though, and glad to see you are making sense of the code 8-).
@CPD this approach is rather procedural in nature. You would have to dispatch a defined event if onChange is not firing when the item is added to the grid. onChange fires when a selected item changes, not necessarily when an item is added.