-
31 Dec 2007 9:23 AM #1
[SOLVED 2.x, 3.x]Grid Drag and Drop reorder rows
[SOLVED 2.x, 3.x]Grid Drag and Drop reorder rows
As of September 6th 2009 here is the latest working version - I confirm that it works as I just used it!
I did not write this plugin, it is the product of other more brighter buttons than me! Looking thru this thread you will see the ones that contributed!
Change Log
sep 6, 2009 - added jamiro's fix for events not firing
Example:
Plugin:Code:{ xtype: "grid", plugins: [new Ext.ux.dd.GridDragDropRowOrder( { copy: true // false by default scrollable: true, // enable scrolling support (default is false) targetCfg: { ... } // any properties to apply to the actual DropTarget })] }
php example of reordering on the server side - you can think of "$views" as rows. Main thing to take note of how we handle things a bit differently when moving something up or down. ['n_order'] is the original order taken from the db or whereeverCode:Ext.namespace('Ext.ux.dd'); Ext.ux.dd.GridDragDropRowOrder = Ext.extend(Ext.util.Observable, { copy: false, scrollable: false, constructor : function(config) { if (config) Ext.apply(this, config); this.addEvents( { beforerowmove: true, afterrowmove: true, beforerowcopy: true, afterrowcopy: true }); Ext.ux.dd.GridDragDropRowOrder.superclass.constructor.call(this); }, init : function (grid) { this.grid = grid; grid.enableDragDrop = true; grid.on({ render: { fn: this.onGridRender, scope: this, single: true } }); }, onGridRender : function (grid) { var self = this; this.target = new Ext.dd.DropTarget(grid.getEl(), { ddGroup: grid.ddGroup || 'GridDD', grid: grid, gridDropTarget: this, notifyDrop: function(dd, e, data) { // Remove drag lines. The 'if' condition prevents null error when drop occurs without dragging out of the selection area if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } // determine the row var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); if (rindex === false || rindex == data.rowIndex) { return false; } // fire the before move/copy event if (this.gridDropTarget.fireEvent(self.copy ? 'beforerowcopy' : 'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections, 123) === false) { return false; } // update the store var ds = this.grid.getStore(); // Changes for multiselction by Spirit var selections = new Array(); var keys = ds.data.keys; for (var key in keys) { for (var i = 0; i < data.selections.length; i++) { if (keys[key] == data.selections[i].id) { // Exit to prevent drop of selected records on itself. if (rindex == key) { return false; } selections.push(data.selections[i]); } } } // fix rowindex based on before/after move if (rindex > data.rowIndex && this.rowPosition < 0) { rindex--; } if (rindex < data.rowIndex && this.rowPosition > 0) { rindex++; } // fix rowindex for multiselection if (rindex > data.rowIndex && data.selections.length > 1) { rindex = rindex - (data.selections.length - 1); } // we tried to move this node before the next sibling, we stay in place if (rindex == data.rowIndex) { return false; } // fire the before move/copy event /* dupe - does it belong here or above??? if (this.gridDropTarget.fireEvent(self.copy ? 'beforerowcopy' : 'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections, 123) === false) { return false; } */ if (!self.copy) { for (var i = 0; i < data.selections.length; i++) { ds.remove(ds.getById(data.selections[i].id)); } } for (var i = selections.length - 1; i >= 0; i--) { var insertIndex = rindex; ds.insert(insertIndex, selections[i]); } // re-select the row(s) var sm = this.grid.getSelectionModel(); if (sm) { sm.selectRecords(data.selections); } // fire the after move/copy event this.gridDropTarget.fireEvent(self.copy ? 'afterrowcopy' : 'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections); return true; }, notifyOver: function(dd, e, data) { var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); // Similar to the code in notifyDrop. Filters for selected rows and quits function if any one row matches the current selected row. var ds = this.grid.getStore(); var keys = ds.data.keys; for (var key in keys) { for (var i = 0; i < data.selections.length; i++) { if (keys[key] == data.selections[i].id) { if (rindex == key) { if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } return this.dropNotAllowed; } } } } // If on first row, remove upper line. Prevents negative index error as a result of rindex going negative. if (rindex < 0 || rindex === false) { this.currentRowEl.removeClass('grid-row-insert-above'); return this.dropNotAllowed; } try { var currentRow = this.grid.getView().getRow(rindex); // Find position of row relative to page (adjusting for grid's scroll position) var resolvedRow = new Ext.Element(currentRow).getY() - this.grid.getView().scroller.dom.scrollTop; var rowHeight = currentRow.offsetHeight; // Cursor relative to a row. -ve value implies cursor is above the row's middle and +ve value implues cursor is below the row's middle. this.rowPosition = e.getPageY() - resolvedRow - (rowHeight/2); // Clear drag line. if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } if (this.rowPosition > 0) { // If the pointer is on the bottom half of the row. this.currentRowEl = new Ext.Element(currentRow); this.currentRowEl.addClass('grid-row-insert-below'); } else { // If the pointer is on the top half of the row. if (rindex - 1 >= 0) { var previousRow = this.grid.getView().getRow(rindex - 1); this.currentRowEl = new Ext.Element(previousRow); this.currentRowEl.addClass('grid-row-insert-below'); } else { // If the pointer is on the top half of the first row. this.currentRowEl.addClass('grid-row-insert-above'); } } } catch (err) { console.warn(err); rindex = false; } return (rindex === false)? this.dropNotAllowed : this.dropAllowed; }, notifyOut: function(dd, e, data) { // Remove drag lines when pointer leaves the gridView. if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-above'); this.currentRowEl.removeClass('grid-row-insert-below'); } } }); if (this.targetCfg) { Ext.apply(this.target, this.targetCfg); } if (this.scrollable) { Ext.dd.ScrollManager.register(grid.getView().getEditorParent()); grid.on({ beforedestroy: this.onBeforeDestroy, scope: this, single: true }); } }, getTarget: function() { return this.target; }, getGrid: function() { return this.grid; }, getCopy: function() { return this.copy ? true : false; }, setCopy: function(b) { this.copy = b ? true : false; }, onBeforeDestroy : function (grid) { // if we previously registered with the scroll manager, unregister // it (if we don't it will lead to problems in IE) Ext.dd.ScrollManager.unregister(grid.getView().getEditorParent()); } });
PHP Code:foreach($views as $k=>$view){
//moving up in order
if($oldIndex > $newIndex){
//this is the one we are changing so we
//just give it the new index
if($view['n_order'] == $oldIndex){
$views[$k]['n_order'] = $newIndex;
//if the order is greater that or equal to the new index it will
//need to be incremented to make room for the change
} else if($view['n_order'] >= $newIndex && $view['n_order'] < $oldIndex){
$views[$k]['n_order'] = $view['n_order']+1;
}
//moving down in order - reverse the above process
} else {
if($view['n_order'] == $oldIndex){
$views[$k]['n_order'] = $newIndex;
} else if($view['n_order'] <= $newIndex && $view['n_order'] > $oldIndex){
$views[$k]['n_order'] = $view['n_order']-1;
}
}
}
Last edited by Lobos; 6 Sep 2009 at 12:37 PM. Reason: Fixed events not firing issue
-
2 Jan 2008 2:55 AM #2
notice the code at the bottom, I got this from another post, but I am not sure if I am moving in the right direction:PHP Code:Ext.BLANK_IMAGE_URL = "js/{.$extjs_version.}/resources/images/default/s.gif";
Ext.onReady(function(){
Ext.QuickTips.init();
function formatDate(value){
return value ? value.dateFormat('M d, Y') : '';
};
// shorthand alias
var fm = Ext.form;
// custom column plugin example
var checkColumn = new Ext.grid.CheckColumn({
header: "Indoor?",
dataIndex: 'indoor',
width: 55
});
// the column model has information about grid columns
// dataIndex maps the column to the specific data field in
// the data store (created below)
var cm = new Ext.grid.ColumnModel([{
id:'common',
header: "Common Name",
dataIndex: 'common',
width: 220,
editor: new fm.TextField({
allowBlank: false
})
},{
header: "Light",
dataIndex: 'light',
width: 130,
editor: new Ext.form.ComboBox({
typeAhead: true,
triggerAction: 'all',
transform:'light',
lazyRender:true,
listClass: 'x-combo-list-small'
})
},{
header: "Price",
dataIndex: 'price',
width: 70,
align: 'right',
renderer: 'usMoney',
editor: new fm.NumberField({
allowBlank: false,
allowNegative: false,
maxValue: 100000
})
},{
header: "Available",
dataIndex: 'availDate',
width: 95,
renderer: formatDate,
editor: new fm.DateField({
format: 'm/d/y',
minValue: '01/01/06',
disabledDays: [0, 6],
disabledDaysText: 'Plants are not available on the weekends'
})
},
checkColumn
]);
// by default columns are sortable
cm.defaultSortable = true;
// this could be inline, but we want to define the Plant record
// type so we can add records dynamically
var Plant = Ext.data.Record.create([
// the "name" below matches the tag name to read, except "availDate"
// which is mapped to the tag "availability"
{name: 'common', type: 'string'},
{name: 'botanical', type: 'string'},
{name: 'light'},
{name: 'price', type: 'float'}, // automatic date conversions
{name: 'availDate', mapping: 'availability', type: 'date', dateFormat: 'm/d/Y'},
{name: 'indoor', type: 'bool'}
]);
// create the Data Store
var store = new Ext.data.Store({
// load using HTTP
url: 'http://localhost/framework/js/ext-2.0/examples/grid/plants.xml',
// the return will be XML, so lets set up a reader
reader: new Ext.data.XmlReader({
// records will have a "plant" tag
record: 'plant'
}, Plant),
sortInfo:{field:'common', direction:'ASC'}
});
// create the editor grid
var grid = new Ext.grid.EditorGridPanel({
store: store,
cm: cm,
renderTo: 'editor-grid',
width:600,
height:300,
autoExpandColumn:'common',
title:'Edit Plants?',
frame:true,
plugins:checkColumn,
clicksToEdit:1,
ddGroup: 'GridDD',
ddText: 'drag and drop to change order',
tbar: [{
text: 'Add Plant',
handler : function(){
var p = new Plant({
common: 'New Plant 1',
light: 'Mostly Shade',
price: 0,
availDate: (new Date()).clearTime(),
indoor: false
});
grid.stopEditing();
store.insert(0, p);
grid.startEditing(0, 0);
}
}]
});
// trigger the data store load
store.load();
});
Ext.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};
Ext.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},
onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},
renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'"> </div>';
}
};
var ddrow = new Ext.dd.DropTarget(grid.getView.mainBody, {
ddGroup : 'GridDD',
copy:false,
notifyDrop : function(dd, e, data){
var sm=grid.getSelectionModel();
var rows=sm.getSelections();
var cindex=dd.getDragData(e).rowIndex;
for (i = 0; i < rows.length; i++) {
rowData=ds.getById(rows[i].id);
if(!this.copy) {
ds.remove(ds.getById(rows[i].id));
ds.insert(cindex,rowData);
}
};
}
});
But unfortunately it is not working for me... the columns drag and drop reorder, but I can't get the rows to do thisPHP Code:var ddrow = new Ext.dd.DropTarget(grid.getView.mainBody, {
ddGroup : 'GridDD',
copy:false,
notifyDrop : function(dd, e, data){
var sm=grid.getSelectionModel();
var rows=sm.getSelections();
var cindex=dd.getDragData(e).rowIndex;
for (i = 0; i < rows.length; i++) {
rowData=ds.getById(rows[i].id);
if(!this.copy) {
ds.remove(ds.getById(rows[i].id));
ds.insert(cindex,rowData);
}
};
}
});
The above is based on the "editior grid" standard example btw. Has anyone got this type of thing working before?
Would it be better for me to use some kind of implementation of the tree instead?
Thanks.
-Lobos
-
2 Jan 2008 11:48 AM #3
-
3 Jan 2008 12:58 AM #4Extensions:
Ext.ux.DatePickerPlus (Multimonth,Multiselect,...)
Ext.ux.menu.StoreMenu - Ajax Store as menu-item config
Extended Window - Aero Shadows, nested grayscaled modal windows
Ext.MessageBox.promptCombo/promptRadio/promptCheckbox
Ext.ux.plugin.triggerfieldTooltip (for Comboboxes, Datefields...)
Ext.util.MD5
Ext.util.Utf8 (encode/decode)
Ext.util.base64 (encode/decode)
Using:
ExtJS 3.4.1.1/4.2
XPsp3/W7sp1
IE8/9/10
FF 20
Chrome 26
-
3 Jan 2008 1:02 AM #5
and I'd love to see. i have this problem too
-
8 Jan 2008 4:19 AM #6
Seems everyone wants to know how to drag and drop reorder grid rows! Like I said before I would just like to know at this stage if it is possible, ie to see an example of this working... from this I am sure I can back engineer the code, but... I first I need to see it!
http://extjs.com/deploy/dev/examples/grid/grouping.html
This would be perfect as an example, ie being able to drag rows into different groups!
-Lobos
-
8 Jan 2008 10:52 PM #7
I'm not sure drag and drop a row to different groups would make sense. For example, since the grid is grouping by industries, if I drag Intel from the computer industry into the food industry with McDonalds, does that make any sense? Probably not.
But I understand and agree with you about someone building a working example of a simple grid drag and drop reordering.
--Colin
-
9 Jan 2008 1:50 AM #8
i agree with colinexl. The better example-base (at first to see ANY kind of solution at all) for dragdrop grid reordering in my pov is the basic array-grid:
http://extjs.com/deploy/dev/examples...rray-grid.htmlExtensions:
Ext.ux.DatePickerPlus (Multimonth,Multiselect,...)
Ext.ux.menu.StoreMenu - Ajax Store as menu-item config
Extended Window - Aero Shadows, nested grayscaled modal windows
Ext.MessageBox.promptCombo/promptRadio/promptCheckbox
Ext.ux.plugin.triggerfieldTooltip (for Comboboxes, Datefields...)
Ext.util.MD5
Ext.util.Utf8 (encode/decode)
Ext.util.base64 (encode/decode)
Using:
ExtJS 3.4.1.1/4.2
XPsp3/W7sp1
IE8/9/10
FF 20
Chrome 26
-
9 Jan 2008 2:15 AM #9
ok, your're right but how to make "basic grid" from example drag and drop grid
-
11 Jan 2008 3:40 PM #10
I got it to work
I got it to work
PHP Code:// this code goes in a javascript include file somewhere
Ext.namespace('Ext.ux.dd');
Ext.ux.dd.GridReorderDropTarget = function(grid, config) {
this.target = new Ext.dd.DropTarget(grid.getEl(), {
ddGroup: grid.ddGroup || 'GridDD'
,grid: grid
,gridDropTarget: this
,notifyDrop: function(dd, e, data){
// determine the row
var t = Ext.lib.Event.getTarget(e);
var rindex = this.grid.getView().findRowIndex(t);
if (rindex === false) return false;
if (rindex == data.rowIndex) return false;
// fire the before move/copy event
if (this.gridDropTarget.fireEvent(this.copy?'beforerowcopy':'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections) === false) return false;
// update the store
var ds = this.grid.getStore();
if (!this.copy) {
for(i = 0; i < data.selections.length; i++) {
ds.remove(ds.getById(data.selections[i].id));
}
}
ds.insert(rindex,data.selections);
// re-select the row(s)
var sm = this.grid.getSelectionModel();
if (sm) sm.selectRecords(data.selections);
// fire the after move/copy event
this.gridDropTarget.fireEvent(this.copy?'afterrowcopy':'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections);
return true;
}
,notifyOver: function(dd, e, data) {
var t = Ext.lib.Event.getTarget(e);
var rindex = this.grid.getView().findRowIndex(t);
if (rindex == data.rowIndex) rindex = false;
return (rindex === false)? this.dropNotAllowed : this.dropAllowed;
}
});
if (config) {
Ext.apply(this.target, config);
if (config.listeners) Ext.apply(this,{listeners: config.listeners});
}
this.addEvents({
"beforerowmove": true
,"afterrowmove": true
,"beforerowcopy": true
,"afterrowcopy": true
});
Ext.ux.dd.GridReorderDropTarget.superclass.constructor.call(this);
};
Ext.extend(Ext.ux.dd.GridReorderDropTarget, Ext.util.Observable, {
getTarget: function() {
return this.target;
}
,getGrid: function() {
return this.target.grid;
}
,getCopy: function() {
return this.target.copy?true:false;
}
,setCopy: function(b) {
this.target.copy = b?true:false;
}
});
PHP Code:// here is an example of how you use it
var grid = new Ext.grid.GridPanel({
ddGroup: 'testDDGroup'
,enableDragDrop: true
,autoHeight: false
,height: 375
,autoScroll: true
,el: 'content_div'
,listeners: {
render: function(g) {
// Best to create the drop target after render, so we don't need to worry about whether grid.el is null
// constructor parameters:
// grid (required): GridPanel or EditorGridPanel (with enableDragDrop set to true and optionally a value specified for ddGroup, which defaults to 'GridDD')
// config (optional): config object
// valid config params:
// anything accepted by DropTarget
// listeners: listeners object. There are 4 valid listeners, all listed in the example below
// copy: boolean. Determines whether to move (false) or copy (true) the row(s) (defaults to false for move)
var ddrow = new Ext.ux.dd.GridReorderDropTarget(g, {
copy: false
,listeners: {
beforerowmove: function(objThis, oldIndex, newIndex, records) {
// code goes here
// return false to cancel the move
}
,afterrowmove: function(objThis, oldIndex, newIndex, records) {
// code goes here
}
,beforerowcopy: function(objThis, oldIndex, newIndex, records) {
// code goes here
// return false to cancel the copy
}
,afterrowcopy: function(objThis, oldIndex, newIndex, records) {
// code goes here
}
}
});
// if you need scrolling, register the grid view's scroller with the scroll manager
Ext.dd.ScrollManager.register(g.getView().getEditorParent());
}
,beforedestroy: function(g) {
// if you previously registered with the scroll manager, unregister it (if you don't it will lead to problems in IE)
Ext.dd.ScrollManager.unregister(g.getView().getEditorParent());
}
}
// ... the rest of the setup for the grid
});
grid.render();
Last edited by clarkke8; 17 Jan 2008 at 3:18 PM. Reason: In the example code, moved creation of the drop target to the grid's "render" event, added ScrollManager unregister call




Reply With Quote
