PDA

View Full Version : [CLOSED] DataView Drag & Drop



softmachine2011
Apr 10, 2013, 2:25 PM
Hi,

I need something like a grid with N columns and N rows, each cell must be drag&drop to another cell of this grid.
Each cell contain three lines of information an a image, that will be editable in a modal window when each item is clicked.
I thought in use a dataview, but I see this thread http://forums.ext.net/showthread.php?16145-Drag-and-drop-reordering-in-Dataview

Is there any automated way to perform this, or I must to implement it like you mention in the thread?

Also, use a dataview it's not priority only an idea. Is there a simple way with a grid or another control?

Any help or idea would be appreciated.

Thanks

Daniil
Apr 10, 2013, 5:54 PM
Hi @softmachine2011,

There is the CellDragDrop plugin for a GridView.
http://docs.sencha.com/ext-js/4-2/#!/api/Ext.ux.CellDragDrop

Example

<%@ Page Language="C#" %>

<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
Store store = this.GridPanel1.GetStore();
store.DataSource = new object[]
{
new object[] { "test1", "test2", "test3" },
new object[] { "test4", "test5", "test6" },
new object[] { "test7", "test8", "test9" }
};
store.DataBind();
}
}
</script>

<!DOCTYPE html>

<html>
<head runat="server">
<title>Ext.NET v2 Example</title>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" />
<ext:GridPanel ID="GridPanel1" runat="server">
<Store>
<ext:Store runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="test1" />
<ext:ModelField Name="test2" />
<ext:ModelField Name="test3" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Test1" DataIndex="test1" />
<ext:Column runat="server" Text="Test2" DataIndex="test2" />
<ext:Column runat="server" Text="Test3" DataIndex="test3" />
</Columns>
</ColumnModel>
<View>
<ext:GridView runat="server">
<Plugins>
<ext:CellDragDrop runat="server" />
</Plugins>
</ext:GridView>
</View>
</ext:GridPanel>
</form>
</body>
</html>

Regarding a DataView. There is no built-in drag&drop. It will require implementing.

Regarding editing. I think it should be possible to implement with either a GridPanel or a DataView.

Hope this helps.

softmachine2011
Apr 11, 2013, 7:41 AM
Well, this plugin exists in ExtJS 4.2 -> Ext.NET 2.2...

As I can see, in ExtJS 4.1.3 -> Ext.NET 2.1, doesn't exists, is there a way to avoid upgrade to Ext.NET 2.2 that doesn't involve to implement it at all manually?

Thanks.

Daniil
Apr 11, 2013, 8:35 AM
I would try to copy the plugin's sources and use it via JavaScript and/or a GenericPlugin.

softmachine2011
Apr 11, 2013, 8:50 AM
Ok thanks, I've been expecting your results and conclusions

Daniil
Apr 11, 2013, 10:04 AM
Well, I said what I would do if I were you:)

I will rephrase. You can try to copy the plugin's sources and use it via JavaScript and/or a GenericPlugin.

Please clarify do you need any assistance on this?

softmachine2011
Apr 11, 2013, 10:54 AM
It seems that there are some new stuff in Ext4.2 that Ext4.1.3 doesn't have, obviously:

Exactly in line 163 of source code


getDragData: function (e) {
var view = this.view,
item = e.getTarget(view.getItemSelector()),
record = view.getRecord(item),
clickedEl = e.getTarget(view.getCellSelector()),
dragEl;



Function getCellSelector doesn't exists

Well it seems that there is no quickly solution, and I must have to implement a custom plugin or something else, is it or have anymore idea?

Daniil
Apr 11, 2013, 5:39 PM
It seems the single thing you should add to get the plugin working.
http://docs.sencha.com/ext-js/4-2/source/Table3.html#Ext-view-Table-method-getCellSelector

getCellSelector

Ext.view.Table.override({
getCellSelector: function(header) {
var result = this.cellSelector;
if (header) {
result += '-' + header.getItemId();
}
return result;
}
});

Example

<%@ Page Language="C#" %>

<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!X.IsAjaxRequest)
{
Store store = this.GridPanel1.GetStore();
store.DataSource = new object[]
{
new object[] { "test1", "test2", "test3" },
new object[] { "test4", "test5", "test6" },
new object[] { "test7", "test8", "test9" }
};
store.DataBind();
}
}
</script>

<!DOCTYPE html>

<html>
<head runat="server">
<title>Ext.NET v2 Example</title>

<script src="resources/js/CellDragDrop.js"></script>
</head>
<body>
<form runat="server">
<ext:ResourceManager runat="server" />
<ext:GridPanel ID="GridPanel1" runat="server">
<Store>
<ext:Store runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="test1" />
<ext:ModelField Name="test2" />
<ext:ModelField Name="test3" />
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel runat="server">
<Columns>
<ext:Column runat="server" Text="Test1" DataIndex="test1" />
<ext:Column runat="server" Text="Test2" DataIndex="test2" />
<ext:Column runat="server" Text="Test3" DataIndex="test3" />
</Columns>
</ColumnModel>
<View>
<ext:GridView runat="server">
<Plugins>
<ext:GenericPlugin runat="server" InstanceName="Ext.ux.CellDragDrop" />
</Plugins>
</ext:GridView>
</View>
</ext:GridPanel>
</form>
</body>
</html>

CellDragDrop.js

Ext.view.Table.override({
getCellSelector: function(header) {
var result = this.cellSelector;
if (header) {
result += '-' + header.getItemId();
}
return result;
}
});


/**
* This plugin can enable a cell to cell drag and drop operation within the same grid view.
*
* Note that the plugin must be added to the grid view, not to the grid panel. For example, using {@link Ext.panel.Table viewConfig}:
*
* viewConfig: {
* plugins: {
* ptype: 'celldragdrop',
*
* // Remove text from source cell and replace with value of emptyText.
* applyEmptyText: true,
*
* //emptyText: Ext.String.htmlEncode('<<foo>>'),
*
* // Will only allow drops of the same type.
* enforceType: true
* }
* }
*/
Ext.define('Ext.ux.CellDragDrop', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.celldragdrop',

uses: ['Ext.view.DragZone'],

/**
* @cfg {Boolean} enforceType
* Set to `true` to only allow drops of the same type.
*
* Defaults to `false`.
*/
enforceType: false,

/**
* @cfg {Boolean} applyEmptyText
* If `true`, then use the value of {@link #emptyText} to replace the drag record's value after a node drop.
* Note that, if dropped on a cell of a different type, it will convert the default text according to its own conversion rules.
*
* Defaults to `false`.
*/
applyEmptyText: false,

/**
* @cfg {Boolean} emptyText
* If {@link #applyEmptyText} is `true`, then this value as the drag record's value after a node drop.
*
* Defaults to an empty string.
*/
emptyText: '',

/**
* @cfg {Boolean} dropBackgroundColor
* The default background color for when a drop is allowed.
*
* Defaults to green.
*/
dropBackgroundColor: 'green',

/**
* @cfg {Boolean} noDropBackgroundColor
* The default background color for when a drop is not allowed.
*
* Defaults to red.
*/
noDropBackgroundColor: 'red',

//<locale>
/**
* @cfg {String} dragText
* The text to show while dragging.
*
* Two placeholders can be used in the text:
*
* - `{0}` The number of selected items.
* - `{1}` 's' when more than 1 items (only useful for English).
*/
dragText: '{0} selected row{1}',
//</locale>

/**
* @cfg {String} ddGroup
* A named drag drop group to which this object belongs. If a group is specified, then both the DragZones and
* DropZone used by this plugin will only interact with other drag drop objects in the same group.
*/
ddGroup: "GridDD",

/**
* @cfg {Boolean} enableDrop
* Set to `false` to disallow the View from accepting drop gestures.
*/
enableDrop: true,

/**
* @cfg {Boolean} enableDrag
* Set to `false` to disallow dragging items from the View.
*/
enableDrag: true,

/**
* @cfg {Object/Boolean} containerScroll
* True to register this container with the Scrollmanager for auto scrolling during drag operations.
* A {@link Ext.dd.ScrollManager} configuration may also be passed.
*/
containerScroll: false,

init: function (view) {
var me = this;

view.on('render', me.onViewRender, me, {
single: true
});
},

destroy: function () {
var me = this;

Ext.destroy(me.dragZone, me.dropZone);
},

enable: function () {
var me = this;

if (me.dragZone) {
me.dragZone.unlock();
}
if (me.dropZone) {
me.dropZone.unlock();
}
me.callParent();
},

disable: function () {
var me = this;

if (me.dragZone) {
me.dragZone.lock();
}
if (me.dropZone) {
me.dropZone.lock();
}
me.callParent();
},

onViewRender: function (view) {
var me = this,
scrollEl;

if (me.enableDrag) {
if (me.containerScroll) {
scrollEl = view.getEl();
}

me.dragZone = new Ext.view.DragZone({
view: view,
ddGroup: me.dragGroup || me.ddGroup,
dragText: me.dragText,
containerScroll: me.containerScroll,
scrollEl: scrollEl,
getDragData: function (e) {
var view = this.view,
item = e.getTarget(view.getItemSelector()),
record = view.getRecord(item),
clickedEl = e.getTarget(view.getCellSelector()),
dragEl;

if (item) {
dragEl = document.createElement('div');
dragEl.className = 'x-form-text';
dragEl.appendChild(document.createTextNode(clicked El.textContent || clickedEl.innerText));

return {
event: new Ext.EventObjectImpl(e),
ddel: dragEl,
item: e.target,
columnName: view.getGridColumns()[clickedEl.cellIndex].dataIndex,
record: record
};
}
},

onInitDrag: function (x, y) {
var self = this,
data = self.dragData,
view = self.view,
selectionModel = view.getSelectionModel(),
record = data.record,
el = data.ddel;

// Update the selection to match what would have been selected if the user had
// done a full click on the target node rather than starting a drag from it.
if (!selectionModel.isSelected(record)) {
selectionModel.select(record, true);
}

self.ddel.update(el.textContent || el.innerText);
self.proxy.update(self.ddel.dom);
self.onStartDrag(x, y);
return true;
}
});
}

if (me.enableDrop) {
me.dropZone = new Ext.dd.DropZone(view.el, {
view: view,
ddGroup: me.dropGroup || me.ddGroup,
containerScroll: true,

getTargetFromEvent: function (e) {
var self = this,
v = self.view,
cell = e.getTarget(v.cellSelector),
row, columnIndex;

// Ascertain whether the mousemove is within a grid cell.
if (cell) {
row = v.findItemByChild(cell);
columnIndex = cell.cellIndex;

if (row && Ext.isDefined(columnIndex)) {
return {
node: cell,
record: v.getRecord(row),
columnName: self.view.up('grid').columns[columnIndex].dataIndex
};
}
}
},

// On Node enter, see if it is valid for us to drop the field on that type of column.
onNodeEnter: function (target, dd, e, dragData) {
var self = this,
destType = target.record.fields.get(target.columnName).type.t ype.toUpperCase(),
sourceType = dragData.record.fields.get(dragData.columnName).ty pe.type.toUpperCase();

delete self.dropOK;

// Return if no target node or if over the same cell as the source of the drag.
if (!target || target.node === dragData.item.parentNode) {
return;
}

// Check whether the data type of the column being dropped on accepts the
// dragged field type. If so, set dropOK flag, and highlight the target node.
if (me.enforceType && destType !== sourceType) {

self.dropOK = false;

if (me.noDropCls) {
Ext.fly(target.node).addCls(me.noDropCls);
} else {
Ext.fly(target.node).applyStyles({
backgroundColor: me.noDropBackgroundColor
});
}

return;
}

self.dropOK = true;

if (me.dropCls) {
Ext.fly(target.node).addCls(me.dropCls);
} else {
Ext.fly(target.node).applyStyles({
backgroundColor: me.dropBackgroundColor
});
}
},

// Return the class name to add to the drag proxy. This provides a visual indication
// of drop allowed or not allowed.
onNodeOver: function (target, dd, e, dragData) {
return this.dropOK ? this.dropAllowed : this.dropNotAllowed;
},

// Highlight the target node.
onNodeOut: function (target, dd, e, dragData) {
var cls = this.dropOK ? me.dropCls : me.noDropCls;

if (cls) {
Ext.fly(target.node).removeCls(cls);
} else {
Ext.fly(target.node).applyStyles({
backgroundColor: ''
});
}
},

// Process the drop event if we have previously ascertained that a drop is OK.
onNodeDrop: function (target, dd, e, dragData) {
if (this.dropOK) {
target.record.set(target.columnName, dragData.record.get(dragData.columnName));
if (me.applyEmptyText) {
dragData.record.set(dragData.columnName, me.emptyText);
}
return true;
}
},

onCellDrop: Ext.emptyFn
});
}
}
});

softmachine2011
Apr 12, 2013, 8:29 AM
Yes, it seems that it accomplish drag and drop as well.

I expected that if I drag a cell to another cell, it swaps both cells. For it I changed onNodeDrop function with this:

onNodeDrop: function (target, dd, e, dragData) {
if (this.dropOK) {
var dropRecordClone = target.record.copy();

target.record.set(target.columnName, dragData.record.get(dragData.columnName));
dragData.record.set(dragData.columnName, dropRecordClone.get(target.columnName));

return true;
}
}

Because I see that onCellDrop is not fired anywhere...

Is it correct or you would change something?

Thanks for your effort.

Daniil
Apr 12, 2013, 9:22 AM
Your change looks correct.

softmachine2011
Apr 12, 2013, 9:48 AM
Ok, but I prefer not to make changes in plugins that next version will provide me, because this implies a future mantenaince.
I prefer use ExtJS 4.2 plugin unchanged and use it normally.

Is there any way to work with onCellDrop listener with generic plugin or I must implement something else to do it?
I don't often use generic plugin, an example would be apreciated.

Thanks.

Daniil
Apr 12, 2013, 12:17 PM
The OnCellDrop event seems to be not executed at all. Maybe, the plugin's creator forgot about it.

I think overriding the onNodeDrop function is the only appropriate solution. If you don't want to override the plugin itself, you can override that function this way.

<ext:GridView runat="server">
<Plugins>
<ext:GenericPlugin runat="server" PluginId="CellDragDrop" InstanceName="Ext.ux.CellDragDrop" />
</Plugins>
<Listeners>
<AfterRender
Handler="this.getPlugin('CellDragDrop').dropZone.onNodeDrop = function (target, dd, e, dragData) {
console.log('onNodeDrop');
};"
Delay="1" />
</Listeners>
</ext:GridView>

softmachine2011
Apr 12, 2013, 12:30 PM
Ok, thanks.

I 'll override this method in this way