ExtJs 4 - Drag from Tree to "empty" tree - javascript

Having a problem with ExtJS when it comes to dragging from a tree to another tree.
I have one tree, sourceTree, that has all the elements available to drag from.
I have another tree, targetTree that will hopefully contain all the elements dragged (selected) from the source tree.
var sourceStore = Ext.create('Ext.data.TreeStore', {
model: 'MyTreeModel',
proxy: {
type: 'ajax',
url: '/some_action_that_returns_tree_json',
extraParams: { /*extra params*/
},
reader: {
type: 'json',
root: function (o) {
if (o.children == null) {
return o.children;
} else {
return o;
}
}
}
},
folderSort: true
});
the source tree is defined as:
{
xtype: 'treepanel',
flex: 5,
iconCls: 'myiconcls',
border: true,
store: formstore,
loadMask: true,
rootVisible: false,
title: 'Source',
bodyStyle: 'font-style:bold',
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
appendOnly: true
}
}
}
So I have a source tree I can drag from.
The target tree is defined as:
{
xtype: 'treepanel',
flex: 5, iconCls: 'mytreecls',
border: true,
store: emptystore,
rootVisible: false,
title: 'Selected',
bodyStyle: 'font-style:bold',
viewConfig: {
emptyText: '',
plugins: {
ptype: 'treeviewdragdrop',
allowContainerDrop: true,
appendOnly: true
},
listeners: {
drop: function (node, data, model, dropPosition, opts) {
console.log(node);
}
}
}
}
The first problem I have is how can I define an empty treestore with a single root node that will call the proxy url if the data for the node is not loaded.
The source tree will load leaf nodes dynamically, if I drag and add them to the target tree I want to keep that functionality.
I've tried lots of ways to set default root node - none of which I've been able to get working.
var emptystore= Ext.create('Ext.data.TreeStore', {
model: 'TreeModel',
proxy: {
type: 'ajax',
url: '/some_action_that_returns_tree_json',
extraParams: { ... },
reader: {
type: 'json',
root: { text:"My Root",...} // doesn't work, presumably becuase the ajax call overwrite it?
}
}
}
});
The end result should be a source tree that I can drag from into another tree. I don't want to remove the dragged node which I think is controlled by appendOnly property of treeviewdragdrop. I was trying to make it so I could handle the drop on the panel/container by setting allowContainerDrop to true.
To be honest this is my first endeavour into ExtJS D&D so any help appreciated. I've obviously read the tutorial but cannot find my on tree to tree D&D.
Thanks in advance,
Sam

Related

How can I handle the exception "cannot call method getRange of null" in ExtJs?

I've searched the net for answer but didn't find anything. Hope you can help.
So I am relativly new to extJs. I have a navigation bar on the left. When I press the first button there, a new window opens, which contains a table and loads its data automatically. The first time it works perfect but when I close the window and open it again I get the error "cannot call method getRange of null".
If I open the second window (when I click the other button in my navigation bar), I have 4 tabs, which contain a table each. Each Tab has a toolbar with buttons (create, change, etc.). Here happens the same thing as by the first window.
I can also print those tables as a List and the first time works fine, but when I cancel the print action I get the error again.
I made sure that all buttons and tables have a different reference, so I really don't know what this could be.
Any ideas?
I add my panels, which will open the new windows here:
items: [
{
xtype: 'tabpanel',
region: 'center',
items: [{
// 1. Tab
xtype: 'componentTap-panel',
title: UMA.Locale.rm.component.componentTitle,
id: 'componentTab'
}, {
// 2. Tab
title: UMA.Locale.rm.componentGroup.componentGroupTitle,
xtype: 'componentGroupTap-panel',
id: 'componentGroupTab'
}, {
// 3. Tab
title: UMA.Locale.rm.componentTemplateTitle,
xtype: 'componentTamplate-panel',
id: 'componentTemplateTab'
},
{
//4.Tab
title: UMA.Locale.rm.inventoryTitle,
xtype: 'inventoryTab-panel',
id: 'inventoryTab'
}
]
}
]
When the window opens I add my table and toolbar:
items: [{
xtype: 'toolbar',
region: 'north',
reference: 'componentToolbar',
items: [{
text: UMA.Locale.rm.buttonCreate,
action: 'createComp'
}, {
text: UMA.Locale.rm.buttonChange,
action: 'changeComp'
}, {
text: UMA.Locale.rm.buttonMove,
action: 'moveComp'
}, {
text: UMA.Locale.rm.buttonDelete,
action: 'deleteComp'
},{
text: UMA.Locale.rm.buttonPrint,
action: 'print',
margin: {
left: 8
}, {
xtype: 'componentTable-panel',
region: 'center'
}, {
xtype: 'componentsFilter-panel',
width: 300,
region: 'east'
}]
and then autoload my table:
items:[{
xtype: 'filtergrid',
reference: 'componentGrid',
paging: true,
hideHeaders: false,
region: 'center',
selModel: new Ext.selection.RowModel({
mode: "MULTI"
}),
store: {
model: 'Component',
autoLoad: true
},
columns: [{ ...
As Sergey Novikov mentioned that getRange() is a store method.
I also faced the same error for my grid's store and after some review again and again I found that whenever I close the tab and reopen it again I am getting two instance of grid's view (which can be checked by grid.getView()) and then I reached a conclusion that whenever the grid is creating the second time the selection model of grid view is having two instances because of I am using the code for selModel as new Ext.selection.CheckboxModel({
showHeaderCheckbox: true,
width: 50,
mode: 'MULTI'
})
then I changed the code for selModel as
selModel: {
selType: 'checkboxmodel',
showHeaderCheckbox: true,
width: 50,
mode: 'MULTI'
}
and the error is gone for me.
Hope this will help you. :)
Use the Object() constructor to test whether the object is one of three subtypes:
null object
object object
array object
For example:
function foo() { return {"hi":"bye" } }
function bar() { return null }
function baz() { return [1,2,3] }
function testObject(myObject)
{
if (Object(myObject).hasOwnProperty("0") )
{
/* Call getRange */
return 'yes';
}
else
{
/* throw an exception */
return 'no';
}
}
console.log(testObject(foo()), testObject(bar()), testObject(baz()) );

Data-binding only part of a DataSource with Kendo UI

I am using OData and Kendo UI to fetch data, expanding into relations, and presenting them in nested Kendo Grids. Because it seems all of this data is part of one data source, whenever any change happens anywhere in the data, the whole grid is regenerated.
I've reproduced the problem using Northwind data here.
I have a master grid:
$("#grid").kendoGrid({
dataSource: {
type: "odata",
transport: {
read: "http://demos.telerik.com/kendo-ui/service/Northwind.svc/Employees?$expand=Orders"
},
pageSize: 6,
serverPaging: true,
serverSorting: true
},
height: 550,
sortable: true,
pageable: false,
editable: "inline",
detailInit: detailInit});
and a detail grid with an add button.
function detailInit(e) {
$("<div/>").appendTo(e.detailCell).kendoGrid({
dataSource: {
data: e.data.Orders.results,
schema: { ... }
},
editable: true,
columns: [ ... ],
toolbar: [{ name: "create", text: "Add Order" }]
});
}
If you expand one of the rows, then click Add Order, the expanded row is immediately collapsed. I've put this down to the entire grid being regenerated.
In Telerik's example this conveniently doesn't happen, a) because they are not editable grids and b) because they are separate endpoints and therefore separate datasources.
Can I force it to only databind the detail grid and prevent this happening?

EXTJS: How can I add an click event on Icon node in a Tree Panel

I try to get an event click on the node's Icon in a tree panel.
I have a tree with many node and in front of the leaf node, I have an Icon.
For the moment, when I click on a node I display a PDF file.
I want to do a specific action when I click on the Icon of this node.
Someone have an idea for do that?
Thanks a lot for your futur answers!
EDIT:
Thanks for your answer,
#Hown_: Ok, but I must do an specific action which depends to the select node. With a CSS selector, I can't do that. I'm wrong?
#budgw: Yes, it's a good solution, but my icon must be in front of the text node :(
I guess the simplest way would be to add itemclick event to your TreePanel and check in the handler fn if the tree icon was clicked by checking the target of the event. It works as simple as this:
You may have to change the css selector of the getTarget fn for your specific use. ( e.g. only leaf elements or pdf icons or something like that )
Ext.onReady(function() {
var store = Ext.create('Ext.data.TreeStore', {
root: {
expanded: true,
children: [{
text: "detention",
leaf: true
}, {
text: "homework",
expanded: true,
children: [{
text: "book report",
leaf: true
}, {
text: "alegrbra",
leaf: true
}]
}, {
text: "buy lottery tickets",
leaf: true
}]
}
});
Ext.create('Ext.tree.Panel', {
title: 'Simple Tree',
width: 200,
store: store,
rootVisible: false,
renderTo: Ext.getBody(),
listeners: {
itemclick: function(treeModel, record, item, index, e, eOpts){
var iconElClicked = e.getTarget('.x-tree-icon');
if(iconElClicked){
//tree icon clicked
//do something in here
console.log('tree icon clicked');
}
}
}
});
});
I don't think you can do that with the icon in front of the node (maybe I'm wrong).
But I usually solve this kind of use case by using a treepanel with 2 columns :
a treecolumn
an action column like in this example here.
The trick is to use the config property 'hideHeaders:true' on the tree panel to make it looks like a classic tree.
You can select the dom elements of the icons via css selector and add a click event after the render method.
here is an example:
https://fiddle.sencha.com/#fiddle/8kd
UPDATE:
exmaple:
Ext.onReady(function() {
var store = Ext.create('Ext.data.TreeStore', {
root: {
expanded: true,
children: [{
text: "detention",
leaf: true
}, {
text: "homework",
expanded: true,
children: [{
text: "book report",
leaf: true
}, {
text: "alegrbra",
leaf: true
}]
}, {
text: "buy lottery tickets",
leaf: true
}]
}
});
Ext.define('MyPanel', {
extend: 'Ext.tree.Panel',
title: 'Simple Tree',
width: 200,
onTreeIconClick: function(treeModel, record, item, index, e, eOpts){
// DO SOMETHING IN HERE
console.log('onTreeIconClicked');
},
render: function(){
this.callParent(arguments);
var domEls = this.el.dom.querySelectorAll('#' + this.getId() + ' .x-tree-icon', this.el.dom);
for(var i = 0; i<domEls.length; i++){
Ext.get(domEls[i]).on('click', function(){
//click on tree icon
this.on('itemclick', this.onTreeIconClick, this, { single: true });
}, this);
}
}
});
Ext.create('MyPanel', {
store: store,
rootVisible: false,
renderTo: Ext.getBody()
});
});

Editor cannot disappear after closing Window

I got a problem on Ext 4.1.0 and Ext 4.1.1
Double click first cell to edit it and then click window close button, the editor still floats on the page.But it is ok for last cell.
Anyone met this problem before? Thanks
Ext.onReady(function(){
Ext.create('Ext.data.Store', {
storeId:'simpsonsStore',
fields:['name', 'email', 'phone'],
data:{'items':[
{ 'name': 'Lisa', "email":"lisa#simpsons.com", "phone":"1224" },
{ 'name': 'Bart', "email":"bart#simpsons.com", "phone":"1234" },
{ 'name': 'Homer', "email":"home#simpsons.com", "phone":"1244" },
{ 'name': 'Marge', "email":"marge#simpsons.com", "phone":"1254" }
]},
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'items'
}
}
});
var table = Ext.create('Ext.grid.Panel', {
title: 'Simpsons',
store: Ext.data.StoreManager.lookup('simpsonsStore'),
columns: [
{
text: 'Name',
dataIndex: 'name',
editor: { xtype: 'textfield', toFrontOnShow: false }
},
{
text: 'Email',
dataIndex: 'email',
flex: 1
},
{
text: 'Phone',
dataIndex: 'phone',
editor: {
xtype: 'numberfield',
hideTrigger: true,
validateOnChange : false
}
}
],
height: 200,
width: 400,
plugins:[ Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2
})]
});
var window = new Ext.Window({
id: 'abc',
title :'abc',
modal : true,
layout: 'border',
resizable : true,
draggable : true,
closable : true,
closeAction : 'hide',
width :410,
height :210,
items : [table]
});
window.show();
});
The easiest way to handle this for you, would be to listen to the window's beforeclose event and cancel any editing in this event using the celleditor's cancelEdit method as described here in the docs.
For example, here is your window object (from your code above) with the listener applied:
var window = new Ext.Window({
id: 'abc',
title :'abc',
modal : true,
layout: 'border',
resizable : true,
draggable : true,
closable : true,
closeAction : 'hide',
width :410,
height :210,
items : [ table],
// add this listener to your window
listeners: {
beforeclose: function(panel) {
var view = panel.down('gridview');
if (view && view.editingPlugin) {
view.editingPlugin.cancelEdit();
}
}
}
});
Reply to comment:
Here's an override that would do the same thing. You would have to include this override in each app after ExtJS initialization though.
Of course it is also possible to replace the init function in the Ext.grid.plugin.Editor source code with this one (then you wouldn't have to include the override in the app) but I wouldn't recommend doing that for a number of reasons.
Ext.override(Ext.grid.plugin.Editor, {
init: function(grid) {
// the normal init code (below) must be included in the override
var me = this;
me.grid = grid;
me.view = grid.view;
me.initEvents();
me.mon(grid, 'reconfigure', me.onReconfigure, me);
me.onReconfigure();
grid.relayEvents(me, [
'beforeedit',
'edit',
'validateedit',
'canceledit'
]);
grid.isEditable = true;
grid.editingPlugin = grid.view.editingPlugin = me;
// additional code to cancel editing before a grid is hidden
grid.on('beforehide', function(grid) {
var view = grid.view;
if (view && view.editingPlugin) {
view.editingPlugin.cancelEdit();
}
});
// additional code to cancel editing before a grid is destroyed
grid.on('beforedestroy', function(grid) {
var view = grid.view;
if (view && view.editingPlugin) {
view.editingPlugin.cancelEdit();
}
});
}
});
I would also recommend looking into MVC architecture, it would make handling things like this alot easier for you.

Getting Model from GridPanel in ExtJS

I have a gridpanel that allows inline editing of a column. This column uses a combobox as the editor, and neither the "change" event nor the "select" event give me something usable to backtrace the edited value to get the changed row from the gridpanel.
I believe Ext floats the editor's combobox so therefore I can't do something simple like
combo.up()
To return to the grid.
Here is the grid panel from the view:
{
xtype: 'gridpanel',
title: 'Important Projects',
id: 'importantProjectsGrid',
dockedItems: [],
flex: 1,
columns: [
{ header: 'Quote Name', dataIndex: 'QuoteName', flex: 4 },
{ header: 'Quote Status', dataIndex: 'QuoteStatusID', flex: 6, editor: {
xtype: 'combobox',
editable: false,
action: 'QuoteStatus',
selectOnTab: true,
store: 'statuses',
queryMode: 'local',
displayField: 'Description',
valueField: 'Description'
} }
],
store: 'myimpprojects',
selModel: {
selType: 'cellmodel'
},
plugins: [Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})]
}
Here is the controller code pertaining to this:
init: function () {
this.control({
'[action=QuoteStatus]': {
change: function (combo, new_value, old_value, opts) {
// I need to go back up from this combobox
// to get the row that this value was edited in
// to grab an ID value from that row's data
// in order to make an ajax request
}
}
});
},
Thanks for any help!
You can monitor store's update event.
init: function () {
this.getMyimpprojectsStore().on('update', function(store, record) {
// do something with record
});
// ...
},
Try putting the listener on the CellEditing plugin. There are events for beforeedit, edit, and validateedit that receive an object containing references to the grid, the record, field, row and column indexes, and more. You should be able to check for the combobox in the event handler and handle your information from there.
Quick link to the doc page: Ext.grid.plugin.CellEditing
I'm convinced that the update plugin will handle the update automatically, through the api of the underlying store and post the data automatically to the server if the proxy as autoSync to true.
Example of the configured proxy:
Ext.define('MyApp.store.YourStore', {
extend: 'Ext.data.Store',
model: 'MyApp.model.YourGridModel',
autoSync: true, //Commits the changes realtime to the server
proxy: {
type: 'ajax',
batchActions : true, //Commits the changes everytime a value is changed if true o otherwise store the changes and batch update them in 1 single post
api: {
read: 'path/to/select',
create: 'path/to/create',
update: 'path/to/update',
destroy: 'path/to/delete'
},
reader: {
type: 'json',
root: 'results',
successProperty: 'success'
},
writer: {
type: 'json',
writeAllFields: true
},
listeners: {
exception: function(proxy, response, operation){
Ext.MessageBox.show({
title: 'REMOTE EXCEPTION',
msg: operation.getError(),
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
},
listeners: {
write: function(proxy, operation){
var response = Ext.JSON.decode(operation.response.responseText);
if(response.success == true)
{
//TODO: Proxy - Messageboxes might be a little anoying we might instead use the status bar in teh grid or something so show status of the operation
Ext.MessageBox.show({
title: this.xFileLibraryTitle,
msg: response.message,
icon: (response.success == true)? Ext.MessageBox.INFO : Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
}
});
I would look specially for the two configs: "autoSync" and "batchActions"
Hope this helps you further with your issue!

Categories