extjs4 tree child item did not show until click on 'sort' header button - extjs

I callled root.appendChild() to add couple children to a treepanel root and called root.expand() to expand the tree but the child nodes did not show until I click on any of the the sort button on the header. Is there any property I need to set to show the child node programmally?
Thanks for help.
The following is the code:
tree.getRootNode().removeAll();
var root = tree.setRootNode({
PRTNUM:'root',
id: 'treeRoot',
leaf: false
});
for (var i = 0; i < result.data.length; i++) {
var rec = result.data[i];
var node = root.appendChild({
PRTNUM: rec.PRTNUM,
DESC: rec.DESC,
icon: this.convertTypeToIcon(rec.TYPE),
id: rec.PRTNUM,
leaf: true
});
}
root.expand();

Since your root node originally had no children it was marked as a leaf.
I found that the following works for parent nodes that are initially leaves and you have to add nodes programatically:
parentNode.set("leaf", false); //must be set to work properly
parentNode.appendChild(newChild);
parentNode.expand();

I found out that adding the following lines after calling appendChild() fix thie 'tree grid did not expand until click on 'sort' column button. Thanks.
root.expandChildren(true);
root.sort(function() {});

Related

Adding Node to TreePanel with a Button. Newbie How To?

I have a button that I put an click event on.
I also have a TreePanel loaded with static JSON.
The TreePanel has an ItemID of projectTree
Inside my button click event I have:
var treeNode = projectTree.getRootNode();
treeNode.expandChildren(true); // Optional: To see what happens
treeNode.appendChild({
name: 'Child 4',
leaf: true,
element : 6
});
treeNode.getChildAt(2).getChildAt(0).appendChild({
name: 'Grand Child 3',
leaf: true,
element: 7
});
It does not add the nodes. What am I doing wrong?
Edited
var treeNode = this.child('#projectTree').getRootNode();
treeNode.expandChildren(true); // Optional: To see what happens
treeNode.appendChild({
name: 'Child 4',
leaf: true,
element : 6
});
// This one does not work
treeNode.childNodes(0).nextSibling.appendChild({
name: 'Grand Child 3',
leaf: true,
element: 7
});
You need to refer to the TreePanel first, you can't just use the itemID as reference. There are many way of doing this, the simplest is to use Ext.getCmp:
var projectTree = Ext.getCmp('projectTree');
This is assuming you only have 1 component with that itemID, if not, it's better to use ComponentQuery, this is also my prefered method. Example, if your TreePanel is in a Container with an itemID "TreePanelContainer":
var projectTree = Ext.ComponentQuery.query('#TreePanelContainer > #TreePanel');
UPDATE:
Is it neccessary to use nextSibling? Why don't you use childNodes[1]?
treeNode.childNodes[1].appendChild({
Fiddle: fiddle.sencha.com/#fiddle/1rb
Also maybe I'm wrong but I've checked the document and there's no element config for TreeNode. Maybe you're using an odler version code. Try the offical documents from Sencha page.

Drag and Drop stops working for an item which is once dragged

I have an ExtJS treepanel in Container A on page and there's another Container B.
The treepanel is initialized with items via Ajax call. In the viewConfig of treepanel, I've added itemadd event listener, in which I register added items as DragSource. Following is the itemadd event listener within viewConfig of treepanel.
'itemadd': function(records, node){
// Iterate over each child record of parent node in treepanel.
Ext.each(records, function(record){
var dragSource,
field,
fieldId;
field = Ext.query('[id='+record.data.listId+']')[0]; // I've manually added 'listId' which has unique value gained from Ext.id() for each record item.
fieldId = field.id;
dragSource = new Ext.dd.DragSource(field, {
isTarget : false
});
dragSource.dragData = {
record: record
};
});
}
And in the items of Container B, I have added a View named MyView which extends Ext.container.Container internally. So in the afterrender of Container B, I'm registering itself as a DropTarget. Here's how I do that;
'afterrender': function(containerMain) {
var dropZone,
myView = Ext.ComponentQuery.query("myview")[0];
dropZone = new Ext.dd.DropTarget(containerMain.getEl()); // Register Container B as DropTarget.
dropZone.notifyDrop = function(source, event, params) {
myView.doSomething(params.record); // This method will handle data of dropped record and internally show something on UI.
};
}
Now the here's problem described in step-by-step usage.
I drag an item from treepanel into Container B for first time and it works fine as intended.
I don't allow to add duplicate items again into MyView, I have a Remove button for each item added to remove it from MyView.
I remove the item which I added.
I try to drag it again from treepanel but it is no longer a draggable item.
Though I can still drag another item from same treepanel and add it, but not the first one which I added and removed earlier (and same thing would happen for all items which I once add to MyView. Note that removing of item is not necessary to reproduce the issue, only adding it causes this.
So what is getting wrong here?
Any help would be great.
I suggest to use the Ext.tree.plugin.TreeViewDragDropView (ptype: treeviewdragdrop) plugin for the tree instead of handling it manually.
Example for the treepanel:
var treePanel = Ext.create('Ext.tree.Panel', {
(...),
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop',
ddGroup: "sameGroup",
enableDrop: false
}
}
});
For the panel:
var panel = Ext.create('Ext.panel.Panel', {
(...),
listeners: {
'afterrender': function(component) {
var dropZone = Ext.create('Ext.dd.DropTarget', component.body.dom, {
ddGroup: 'sameGroup',
notifyDrop: function(source, event, params) {
var myView = panel.up('myview');
myView.doSomething(source, event, params);
return true;
}
});
}
}
});

TreePanel not expanding ExtJS 3

This is my code -
var myTree = {
containerScroll: "true",
width: 500,
root: new Ext.tree.AsyncTreeNode({
id: "source",
text: "Root",
expanded: true,
draggable: true,
expandable: true
}),
loader: new Ext.tree.TreeLoader({
dataUrl: 'page.php?action=Get_Tree',
preloadChildren: true,
expandAll: function () {}
}),
xtype: "treepanel",
loadMask: {
msg: 'Loading...'
},
maskDisabled: false,
id: "tree",
listeners: {
click: function (node, event) {
// Render entity data in right panel
handleAction(node);
}
}
}
when user clicks on any node of Tree, I fetch the data from DB and rendering the another panel using handleAction method. In my case, I need to show node's info in another panel and update it. On update handler, I need to change node's text/id as its updated now.
This works fine but then I need to refresh the tree so that, I can view updated node. But, it doesn't work for me :(.
This is my code for updating node -
var tree = Ext.getCmp('tree');
var id = req.responseText.split('#')[1];
var myId = 'Spec#' + entityId;
var node = tree.getNodeById(myId);
node.id = 'Spec#' + id;
node.text = updated_spec_id;
tree.enable();
tree.getLoader().dataUrl = 'page.php?todo=Get_Tree';
tree.getLoader().load(tree.root);
// This I have tried but not succeed.
tree.getView().refresh();
tree.expandAll();
node.expand();
tree.getRootNode().expand(true);
However, when I manually expand it using mouse click, node is updated.
Please help. Thanks in advance.
Have you tried doLayout function? Excerpt from doc:
If the Container is already rendered when add is called, you may need to call doLayout to refresh the view which causes any unrendered child Components to be rendered. This is required so that you can add multiple child components if needed while only refreshing the layout once.
It seems to me, your tree doing normal behaviour. Because, you had used click event, therefore this event fire when you click the node.

show dynamic data using checkbox in extjs

I have a combo box in which the user selects a value, that value is passed to the checkbox data store and it is populated dynamically from database (oracle). I tried the code below. It seems that the selected parameter is being passed to checkbox and I can see the data being populated on the console. I just can't render the checkbox on form. The error I get is: typeError: this.items[0] is undefined.
testArray = new Array();
var testStore = new Ext.data.JsonStore({
proxy:new Ext.data.HttpProxy({
method:'GET',
prettyUrls:false,
url:'kiu.htm',
listeners:{
'loadexception':{
fn:test.form.data.loadException
}
}
}),
fields:["id", "display"],
reader:new Ext.data.JsonReader({
id:'id',
root:'results',
totalProperty:'totalCount',
fields:new Ext.data.Record.create([
{name:'id',type:'int'},
{name:'display',type:'string'}
])
}),
listeners:{
load: function(t, records, options, success) {
for(var i=0; i<records.length; i++) {
testArray.push({name:records[i].data.id, boxLabel: records[i].data.display});
alert(testArray[i].id);
}
}
}
});
{
xtype:'combo',
id:'comboid3',
store:combostore,
displayField:'display',
valueField:'id',
tabIndex:1,
loadingText:'Loading combo...',
listeners :{
select:function(event){
testStore.baseParams = {
"comboid":Ext.getCmp('comboid3').getValue()
};
testStore.load();
}
}
},
{
xtype:'checkboxgroup',
fieldLabel:'Check',
items:testArray
}
Help will be appreciated!
Specifying the Ext JS version is always going to be helpful. It appears this must be 2.x or 3.x and not the current version.
The issue is timing, load calls are asynchronous so by attempting to utilize testArray in this fashion you are likely referencing an empty array by the time it parses the items property of your checkboxgroup. You have a couple options around this... one is to grab a reference to the checkbox group and add the items into it, the other is to not put the checkbox group in the form at all until the call returns and then add it with the populated items array. In either case it is likely that you will need to look up a component reference to either the FormPanel or the CheckboxGroup from within the load handler function and call the 'add' method to add child items.

Extended ExtJS Class can't find custom listener function - "oe is undefined"

I'm adding a custom context menu to a TreePanel.
This was all working when I had a separate function for the context menu, but I was having problems where the context menu items would end up doubled/tripling up if I clicked on one of the options and then viewed the context menu again.
I had a look around for other contextmenu examples and came up with this one by Aaron Conran I pretty much "stole" it wholesale with a few additions, tacking the function directly into the Ext.ext.treePanel config. This gave me an error about "oe is undefined" which seemed to refer to "contextmenu: this.onContextMenu" in the tree config.
I figured it was probably something to do with the way I was defining all of this, so I decided to look at extending Ext.ext.TreePanel with my function in it as a learning exercise as much as anything.
Unfortunately, having managed to sort out extending TreePanel I'm now back to getting "oe is undefined" when the page tries to build the TreePanel. I've had a look around and I'm not really sure whats causing the problem, so any help or suggestions would be greatly appreciated.
Here is the code that is used to define/build the tree panel. I hope its not too horrible.
siteTree = Ext.extend(Ext.tree.TreePanel,{
constructor : function(config){
siteTree.superclass.constructor.call(this, config);
},
onContextMenu: function(n,e){
if (!this.contextMenu){
console.log('treeContextMenu',n,e);
if (n.parentNode.id == 'treeroot'){
var menuitems = [{text:'Add Child',id:'child'}];
} else {
var menuitems =
[{text:'Add Child',id:'child'},
{text:'Add Above',id:'above'},
{text:'Add Below',id:'below'}];
}
this.contextMenu = new Ext.menu.Menu({
id:'treeContextMenu',
defaults :{
handler : treeContextClick,
fqResourceURL : n.id
},
items : menuitems
});
}
var xy = e.getXY();
this.contextMenu.showAt(xy);
}
});
var treePanel = new siteTree({
id: 'tree-panel',
title : 'Site Tree',
region : 'center',
height : 300,
minSize: 150,
autoScroll: true,
// tree-specific configs:
rootVisible: false,
lines: false,
singleExpand: true,
useArrows: true,
dataUrl:'admin.page.getSiteTreeChildren?'+queryString,
root: {
id: 'treeroot',
nodeType: 'async',
text: 'nowt here',
draggable: false
},
listeners:{
contextmenu: this.onContextMenu
}
});
As a total aside; Is there a better way to do this in my context menu function?
if (n.parentNode.id == 'treeroot') {
Basically, if the clicked node is the top level I only want to give the user an add Child option, not add above/below.
Thanks in advance for your help
In your instantiation of your siteTree class you have:
listeners: {
contextmenu: this.onContextMenu
}
However, at the time of the instantiation this.onContextMenu is not pointing to the onContextMenu method you defined in siteTree.
One way of fixing it is to call the method from within a wrapper function:
listeners: {
contextmenu: function() {
this.onContextMenu();
}
}
Assuming you don't override the scope in the listeners config 'this' will be pointing to the siteTree instance at the time the listener is executed.
However, since you are already defining the context menu in the siteTree class, you may as well define the listener there:
constructor: function( config ) {
siteTree.superclass.constructor.call(this, config);
this.on('contextmenu', this.onContextMenu);
}
Ensuring the context menu is removed with the tree is also a good idea. This makes your siteTree definition:
var siteTree = Ext.extend(Ext.tree.TreePanel, {
constructor: function( config ) {
siteTree.superclass.constructor.call(this, config);
this.on('contextmenu', this.onContextMenu);
this.on('beforedestroy', this.onBeforeDestroy);
},
onContextMenu: function( node, event ) {
/* create and show this.contextMenu as needed */
},
onBeforeDestroy: function() {
if ( this.contextMenu ) {
this.contextMenu.destroy();
delete this.contextMenu;
}
}
});
I had this problem yesterday. The issue with the duplicate and triplicate items in the context menu is due to extjs adding multiple elements to the page with the same ID. Each time you call this.contextMenu.showAt(xy) you are adding a div with the ID 'treeContextMenu' to the page. Most browsers, IE especially, deal with this poorly. The solution is to remove the old context menu before adding the new one.
Here is an abridged version of my code:
var old = Ext.get("nodeContextMenu");
if(!Ext.isEmpty(old)) {
old.remove();
}
var menu = new Ext.menu.Menu({
id:'nodeContextMenu',
shadow:'drop',
items: [ ... ]
});
menu.showAt(e.xy);
I suggest never using hardcoded IDs. #aplumb suggests cleaning the DOM to reuse an existing ID. OK, but I suggest you cleanup the DOM when you no longer need the widgets/elements in the DOM and you should never reuse an ID.
var someId = Ext.id( null, 'myWidgetId' );
var someElement = new SuperWidget({
id: someId,
...
});
Just to add to owlness's answer
This bit here:
listeners: {
contextmenu: this.onContextMenu
}
Gets executed when the javascript file is loaded. this at that stage is most likely pointing to the window object.
A simple way to fix it is adding the listener on hide event of context menu, so you destroy him.
new Ext.menu.Menu(
{
items:[...],
listeners: { hide: function(mn){ mn.destroy(); } }
}
).show(node.ui.getAnchor());
;)

Resources