ext js drag drop dataview determine node dropped on - extjs

I am using ext js to drag and drop data from one dataview to another. I want to know if the drop event happened on top of an existing node or if it was just dropped in the white space of the dataview.
Here's the code for my dropTarget:
...
onDesktopDataViewRender: function (v) {
var dataView = v;
v.dropTarget = Ext.create('Ext.dd.DropTarget', v.el, {
ddGroup: 'FromSearchToDesktop',
notifyDrop: function (source, e, dropData) {
//Want do do something like:
//if(dropped directly on any node) {
// do some logic with that node
//}
//else {
// do the code below
var recordAlreadyExists = false;
v.store.each(function (r) {
if (r.data.ID == dropData.searchData.ID) {
recordAlreadyExists = true;
}
});
if (recordAlreadyExists == false) {
v.store.add(dropData.searchData);
}
//end else
}
});
}
...

Yay! Finally figured this one out.
The quick answer is to create a DropZone for the node. The long answer is how to do that.
In my program, the user can drag items from DataView A to DataView B. After dropping the item in DataView B, the item appears in DataView B. On top of that, the user can drag an item from DataView A, and drop it on a node inside DataView B. The code needs to differentiate between the item being dropped on the DataView and the item being dropped on a node inside the DataView.
Generic instructions:
In DataViewB's onrender event, create a dropTarget with a ddGroup of
"DataViewB"
Inside the notifyDrop function, create a new node.
Also inside the notifyDrop function, create another dropTarget
(this one for the node instead of the DataView) with a ddGroup of "DataViewBNode".
Inside DataViewA's onRender event, create a DragZone with a ddGroup
of "DataViewBNode" (!important!)
Inside of DataViewA's afterrender event, add the dragZone to the
"DataViewB" group.
Now you will be able to drag from DataViewA and drop in the white space of DataViewB to add a node, but you will also be able to drop directly on a node from DataViewB and do a different action.
It's very important that the first ddGroup is for the node, and the one that's added in the afterrender event is for the DataView
Here is the code for DataView A:
onDataViewARender: function (v) {
var dataView = v;
...
v.dragZone = Ext.create('Ext.dd.DragZone', v.getEl(), {
ddGroup: 'DataViewBNode',
getDragData: function (e) {
var sourceEl = e.getTarget(v.itemSelector, 10), d;
if (sourceEl) {
d = sourceEl.cloneNode(true);
d.id = Ext.id();
return v.dragData = {
sourceEl: sourceEl,
repairXY: Ext.fly(sourceEl).getXY(),
ddel: d,
searchData: v.getRecord(sourceEl).data,
store: v.store,
source: 'DataViewA'
}
}
},
getRepairXY: function () {
return this.dragData.repairXY;
}
});
},
onDataViewAAfterRender: function(v) {
var dragZone = v.dragZone;
dragZone.addToGroup('DataViewB');
},
Here is the code for DataViewB
onDataViewBRender: function (v) {
var dataView = v;
v.dropTarget = Ext.create('Ext.dd.DropTarget', v.el, {
ddGroup: 'DataViewB',
notifyDrop: function (source, e, dropData) {
var recordAlreadyExists = false;
v.store.each(function (r) {
if (r.data.ID == dropData.searchData.ID && r.data.Type == dropData.searchData.Type) {
recordAlreadyExists = true;
}
});
if (recordAlreadyExists == false) {
v.store.add(dropData.searchData);
var nodes = v.container.dom.childNodes[0].childNodes;
var index = v.container.dom.childNodes[0].childNodes.length -1;
//
//Here is where you create the dropTarget for the new node
//
nodes[index].dropTarget = Ext.create('Ext.dd.DropTarget', nodes[index], {
ddGroup: 'DataViewBNode',
notifyDrop: function (source, e, dropData) {
console.log('success')
}
});
}
}
});
...
},

Related

Simulate Enter key on clicking space key

I want to select and deselct combobox items using space key. In the existing functionality it happens with Enter key.
I have the following code which is not working and giving error deep inside the sencha files.Here me is the combobox.
keydown: function (me, e, eOpts) {
me.keyPressed = e.getKey();
if (me.keyPressed == 32) {
e.keyCode = Ext.EventObject.ENTER;
this.fireEvent(e);
}
}
The way to go is to modify the key bindings of the KeyNavigation of the picker.
listeners:{
afterrender:function(combo) {
var picker = combo.getPicker(),
navModel = picker.getNavigationModel(),
map = navModel.keyNav.map,
existingBindings = Ext.Array.filter(map.bindings, function(binding) {
return binding.key === Ext.event.Event.SPACE;
};
map.removeBinding(existingBindings[0]);
map.addBinding({
key: Ext.event.Event.SPACE,
fn: navModel.onKeyEnter,
scope: navModel
});
}
}

Extjs - drag drop restriction, containment

In Extjs, I want to know whether I can restrict the dragging of elements within a specific x,y co-ordinates, just like an option, containment in jQuery-UI.
Currently this is my code:
abc.prototype.initDrag = function(v) {
v.dragZone = new Ext.dd.DragZone(v.getEl(), {
containerScroll : false,
getDragData : function(e) {
var sourceEl = e.getTarget(v.itemSelector, 10);
var t = e.getTarget();
var rowIndex = abc.grid.getView().findRowIndex(t);
var columnIndex = abc.grid.getView().findCellIndex(t);
var abcDragZone = v.dragZone ; //Ext.getCmp('idabcDragZone');
var widthToScrollV = $(window).width()-((columnIndex-1)*100);
var widthToScrollH = $(window).height()-((5-rowIndex)*30);
abcDragZone.setXConstraint(0,widthToScrollV);
abcDragZone.setYConstraint(widthToScrollH,0);
if ((rowIndex !== false) && (columnIndex !== false)) {
if (sourceEl) {
abc.isDragged = true;
def.scriptGrid.isDraggableForObject = false;
def.scriptGrid.dragRowIndex = false;
d = sourceEl.cloneNode(true);
d.id = Ext.id();
d.textContent = "\$" + abc.grid.getColumnModel().getColumnHeader(columnIndex);
return {
ddel : d,
sourceEl : d,
sourceStore : v.store
}
}
}
},
getRepairXY : function() {
return this.dragData.repairXY;
},
});
}
But the problem is that the initdrag is called when the csv sheet is added to DOM. Only when its added that element can be accessed and the individual cells' drag limits can be set. So once I add a csv, the limits are not getting set. If I add it again to DOM then the limits work. Is there an option like the jQuery UI containment for draggable, here in extjs?
edit:
I even tried :
constrainTo( constrainTo, [pad], [inContent] )
body had an id of #abc
when I tried with
dragZoneObj.startDrag = function(){
this.constrainTo('abc');
};
which is a method of the DragZone class. It still did not cover the whole body tag.

ExtJS - Drag column header to other grid

I need keep the default feature of reordering columns and add
possibility drop the column in a second grid, building in the last a list
with the columns of first grid.
I hope has been clear.
I solved the issue extending DropZone. This implementation receive as constructor param the target grid, and this, be in the rbar (docked control) of source grid. The key is set ddGroup to "header-dd-zone-" plus id from source grid. I hope this is useful.
Ext.define('Crud.FilterDropZone', {
extend: 'Ext.dd.DropZone'
, constructor: function() {}
, init: function (grid) {
var me = this;
if (grid.rendered) {
me.grid = grid;
me.ddGroup = 'header-dd-zone-' + grid.up('grid').id;
grid.getView().on({
render: function(v) {
me.view = v;
Crud.FilterDropZone.superclass.constructor.call(me, me.view.el);
},
single: true
});
} else {
grid.on('render', me.init, me, {single: true});
}
}
, getTargetFromEvent: function (e) {
return {};
}
, onNodeDrop: function (nodeData, source, e, data) {
var header = data.header
, store = Ext.getCmp(e.target.id).getStore();
//store.add(new store.RecordType({ property: header.text, value: '', reference: header.dataIndex}));
store.add([[header.text, '', header.dataIndex]]);
}
});

ExtJs 4.1 TreeGrid Filter through Column Headers

I've been searching around and looking into the documentation, but I'm not sure how to activate filtering via the column drop down the way you have it in normal grid panels.
I've tried to implement the ux.Grid.FilterFeatures but when I apply it to the tree panel, my panel doesn't render properly (all blue panel). I thought it might have something to do with its deferred layout, but when I do a treegrid.hide()/treegrid.show()/treegrid.doLayout(), it doesn't make a difference.
Has anyone gotten the filter feature working with the treepanel? Or has anyone got any suggestions on how to rectify this problem?
I modified the filter example in the Ext-Js site to use it in tree grid.
Ext.define('TreeGridFilter', {
extend: 'Ext.grid.feature.Feature'
, alias: 'feature.treeGridFilter'
, collapseOnClear: true // collapse all nodes when clearing/resetting the filter
, allowParentFolders: false // allow nodes not designated as 'leaf' (and their child items) to be matched by the filter
, treeGrid: null
, filterPropertyNames: new Array()
, filterPropertyValues: new Array()
, filterColumnRenderers: new Array()
, init: function (tree) {
var me = this;
treeGrid = me.tree = tree;
var view = me.view;
var headerCt = view.headerCt;
// Listen for header menu being created
headerCt.on('menucreate', me.onMenuCreate, me);
tree.filter = Ext.Function.bind(me.filter, me);
tree.clearFilter = Ext.Function.bind(me.clearFilter, me);
}
,filter: function (value, property, re, columnRenderer) {
var me = this
, tree = me.tree
, matches = [] // array of nodes matching the search criteria
, root = tree.getRootNode() // root node of the tree
, property = property || 'text' // property is optional - will be set to the 'text' propert of the treeStore record by default
, visibleNodes = [] // array of nodes matching the search criteria + each parent non-leaf node up to root
, viewNode;
me.updateValueForName(property, value, columnRenderer);
if (me.filterPropertyNames.length == 0) { // if the search field is empty
me.clearFilter();
return;
}
tree.expandAll(); // expand all nodes for the the following iterative routines
//iterate over all nodes in the tree in order to evalute them against the search criteria
root.cascadeBy(function (node) {
var numberOfFiltersMatched = 0;
for (var index=0; index < me.filterPropertyNames.length; index++)
{
var propertyName = me.filterPropertyNames[index];
var propertyValue = me.filterPropertyValues[index]
var propertyValueOfNode = node.get(propertyName);
if(me.filterColumnRenderers[index] != false){
var renderingFunction = me.filterColumnRenderers[index];
propertyValueOfNode = renderingFunction(propertyValueOfNode); //Using the renderer function of the column
}
var regExpn = new RegExp(propertyValue, "ig") // the regExp could be modified to allow for case-sensitive, starts with, etc.
if(propertyValueOfNode != null && (propertyValueOfNode+'').match(regExpn)) {
numberOfFiltersMatched++;
}
}
if(numberOfFiltersMatched == me.filterPropertyNames.length){
matches.push(node); // add the node to the matches array
}
});
if (me.allowParentFolders === false) { // if me.allowParentFolders is false (default) then remove any non-leaf nodes from the regex match
Ext.each(matches, function (match) {
if (match == null || !match.isLeaf()) {
Ext.Array.remove(matches, match);
}
});
}
Ext.each(matches, function (item, i, arr) { // loop through all matching leaf nodes
root.cascadeBy(function (node) { // find each parent node containing the node from the matches array
if (node.contains(item) == true) {
visibleNodes.push(node); // if it's an ancestor of the evaluated node add it to the visibleNodes array
}
});
if (me.allowParentFolders === true && !item.isLeaf()) { // if me.allowParentFolders is true and the item is a non-leaf item
item.cascadeBy(function (node) { // iterate over its children and set them as visible
visibleNodes.push(node);
});
}
visibleNodes.push(item); // also add the evaluated node itself to the visibleNodes array
});
root.cascadeBy(function (node) { // finally loop to hide/show each node
viewNode = Ext.fly(tree.getView().getNode(node)); // get the dom element assocaited with each node
if (viewNode) { // the first one is undefined ? escape it with a conditional
viewNode.setVisibilityMode(Ext.Element.DISPLAY); // set the visibility mode of the dom node to display (vs offsets)
viewNode.setVisible(Ext.Array.contains(visibleNodes, node));
}
});
}
, clearFilter: function () {
var me = this
, tree = this.tree
, root = tree.getRootNode();
if (me.collapseOnClear) {
tree.collapseAll(); // collapse the tree nodes
}
root.cascadeBy(function (node) { // final loop to hide/show each node
viewNode = Ext.fly(tree.getView().getNode(node)); // get the dom element assocaited with each node
if (viewNode) { // the first one is undefined ? escape it with a conditional and show all nodes
viewNode.show();
}
});
},
onMenuCreate: function(headerCt, menu) {
var me = this;
menu.on('beforeshow', me.onMenuBeforeShow, me);
},
onMenuBeforeShow: function(menu) {
var me = this;
var currentHeaderFilter = menu.activeHeader.filter;
if(currentHeaderFilter == null){
if(me.menuItem == null){
return;
}
me.menuItem.hide();
me.menuSeparator.hide();
}else if(me.menuItem != null){
me.menuItem.show();
me.menuSeparator.show();
}
if(me.menuItem){
var perviousFilterValue = me.getValueForName(menu.activeHeader.dataIndex);
if(perviousFilterValue == null || perviousFilterValue == ''){
me.menuItem.setRawValue('');
}else{
me.menuItem.setRawValue(perviousFilterValue);
}
}else{
me.menuSeparator = menu.add('-');
var filterTextFiels = new Ext.form.TextField({
itemId: 'filterTextBox',
cls : 'find-icon',
listeners: {
'change': this.onFilterTextChange
}
});
me.menuItem = menu.add(filterTextFiels);
}
me.menuItem.activeDataIndex = menu.activeHeader.dataIndex;
me.menuItem.activeRenderer = menu.activeHeader.renderer;
me.menuItem.width = (currentHeaderFilter == null || currentHeaderFilter.width == null) ? 150 : currentHeaderFilter.width;
},
onFilterTextChange : function (searchMenuItem, value) {
treeGrid.filter(value,searchMenuItem.activeDataIndex, null, searchMenuItem.activeRenderer);
},
updateValueForName : function(property, value, columnRenderer){
var propertyIndex = -1;
for (var index=0; index < this.filterPropertyNames.length; index++)
{
if(property == this.filterPropertyNames[index]){
propertyIndex = index;
break;
}
}
if(propertyIndex >= 0){
if(value == null || value == ''){
this.filterPropertyNames.splice(propertyIndex, 1);
this.filterPropertyValues.splice(propertyIndex, 1);
this.filterColumnRenderers.splice(propertyIndex, 1);
}else{
this.filterPropertyValues[propertyIndex] = value;
}
}else{
propertyIndex = this.filterPropertyNames.length;
this.filterPropertyNames[propertyIndex] = property;
this.filterPropertyValues[propertyIndex] = value;
this.filterColumnRenderers[propertyIndex] = columnRenderer;
}
},
getValueForName : function(property){
var propertyIndex = -1;
for (var index=0; index < this.filterPropertyNames.length; index++)
{
if(property == this.filterPropertyNames[index]){
propertyIndex = index;
break;
}
}
if(propertyIndex >= 0){
return this.filterPropertyValues[propertyIndex];
}else{
return null;
}
}
});
This feature can be used in grid as
var treeGridFilter = {
ftype: "treeGridFilter"
};
var treeGrid= Ext.create('Ext.tree.Panel', {
id : 'tree-grid-id',
title: 'View Tree Grid',
features: [treeGridFilter],
store: store,
renderTo: 'grid-div',
columns: [{
text: 'Column One',
dataIndex: 'columnOne',
filter : {
width: 150
}
},{
text: 'Column Two',
dataIndex: 'columnTwo',
filter : {
width: 100
}
}]
});
Tree stores don't have a filtering function, but I had to create one for an earlier project. Here's an example.
So, as a suggestion you could create your own filter drop menus using Ext.grid.header.Container configs in the tree columns (as covered here in the docs) these could be set-up to call some kind of filter function on the treestore like the one I linked to above.

ExtJS - EditorGridPanel rows not draggable?

I had a working grid-to-grid drag and drop configuration, but once I changed one of the grids to an EditorGridPanel, I could no longer drag from it - only to it. Once I click on the row I want to drag I get the following error:
sm.isSelected is not a function
if(!sm.isSelected(rowIndex) || e.hasModifier()){
ext-all-debug.js (line 45439)
Is there any way to set it up so I can drag rows from an EditorGridPanel?
Found the answer to this inadvertently while looking at another example.
When creating a EditorGridPanel, be sure to include:
selModel:new Ext.grid.RowSelectionModel({singleSelect:true}),
to get the drag and drop functionality to work.
You don't have to use RowSelectionModel, but you will have to write your own code for the drag and drop zones. Here's an example of how to use drag and drop with CellSelectionModel:
grid1.on('render', function(grid) {grid1.initializeDragZone (grid1); });
grid2.on('render', function(grid) {grid2.initializeDropZone (grid2); });
initializeDragZone : function(grid) {
grid.dragZone = new Ext.dd.DragZone(grid.getEl(), {
getRepairXY: function() {
return this.dragData.repairXY;
},
getDragData: function(e) {
var cell = grid.getSelectionModel().getSelectedCell();
var row = grid.getStore().getAt(cell[0]);
var data = row.get('id'); //you can put custom data here
var sourceEl = grid.getView().getRow(cell[0]);
if (sourceEl) {
var d = sourceEl.cloneNode(true);
d.id = Ext.id();
return grid.dragData = {
sourceEl: sourceEl,
repairXY: Ext.fly(sourceEl).getXY(),
ddel: d,
customData: data //our custom data
};
}
}
});
}
initializeDropZone : function(grid) {
grid.dropZone = new Ext.dd.DropZone(grid.getView().scroller, {
getTargetFromEvent: function(e) { //supports multiple drop zone classes
var target = e.getTarget('.some-class1');
target = target? target: e.getTarget('.some-class2');
target = target? target: e.getTarget('.some-class3');
target = target? target: e.getTarget('.some-class4');
return target;
},
onNodeEnter : function(target, dd, e, data){
Ext.fly(target).addClass('drop-zone-selected'); //test
},
onNodeOut : function(target, dd, e, data){
Ext.fly(target).removeClass('drop-zone-selected'); //test
},
onNodeOver : function(target, dd, e, data){
return Ext.dd.DropZone.prototype.dropAllowed;
},
onNodeDrop : function(target, dd, e, data){
var rowIndex = grid.getView().findRowIndex(target);
var rowRecord = grid.getStore().getAt(rowIndex);
var customData = data.customData;
//use custom data
return true;
}
});
}

Resources