PDA

View Full Version : New approach to a tree-grid component



teqneers
12 Mar 2010, 3:26 AM
Hi all,

We recently had to implement a user interface that included a editable grid displaying a tree-structure. Naturally we first turned our attention to the Ext.ux.tree.TreeGrid which was officially released with ExtJS 3.1. Sadly this component lacks the current high standard set by ExtJS components such as the Ext.grid.GridPanel, Ext.tree.TreePanel or the whole layout engine. Even worse it also lacks any documentation at all - just a side note (or a broad hint to de developers ;-)

In our use-case, especially the column-model that's based on the Ext.list.ListView column-model, which made implementing editable fields quite hard and ugly, and some problems with calculating column widths as well as the lack of support for auto-expanding columns (we could correct the last two things by "hacking" the respective methods) proved to be a real show-stopper, so we started to think the other way 'round: why not use the current high-quality Ext.grid.GridPanel and Ext.grid.EditorGridPanel and extend them to be able to display tree-structures?

On the basis of the componentized structure (panel component, store, column model, view and selection model) of Ext.grid.GridPanel we identified that it should be enough to just implement our own store that's able to read tree-like-structures and our own view that modifies row and cell rendering to handle alls those tree-features. That said, implementation went quite smooth, even though we had to copy some larger code fragments from the overridden base classes. The following code is by no means complete or perhaps it's not even production-ready outside our own application (as it doesn't support adding and removing or reordering nodes yet), but we think that this approach would be much more flexible and usable than using the current Ext.ux.tree.TreeGrid. Perhaps the ExtJS developers could give that approach a try...

The first step was to turn a tree-structure into a set of records usable by the grid component:



Ext.ux.tree.TreeReader = Ext.extend(Ext.data.DataReader, {

tree: null,

constructor: function(meta, recordType) {
Ext.ux.tree.TreeReader.superclass.constructor.call(this, meta, recordType || meta.fields);
},

load: function(node){
if (node.attributes.children){
var cs = node.attributes.children;
for (var i = 0, len = cs.length; i < len; i++){
var cn = node.appendChild(this.createNode(cs[i]));
this.load(cn);
}
}
},

createNode: function(attr){
var node = new Ext.data.Node(attr);
node.expanded = (attr.expanded === true);
return node;
},

/**
* Create a data block containing Ext.data.Records from a tree.
*/
readRecords: function(o) {

var root = this.createNode({
text: 'Root',
id: 'root',
children: o
});
this.tree = new Ext.data.Tree(root);
this.load(root);

var f = this.recordType.prototype.fields;
var records = [];
root.cascade(function(node) {
if (node !== root) {
var record = new this.recordType(this.extractValues(node, f.items), node.id);
record.node = node;
record.depth = node.getDepth();
records.push(record);
}
}, this);

return {
success : true,
records : records,
totalRecords : records.length
};
},

/**
* type-casts a single node
*/
extractValues : function(node, fields) {
var f, values = {};
for(var j = 0; j < fields.length; j++){
f = fields[j];
var v = node.attributes[f.mapping];
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, node);
}
return values;
}
});


This allows us to simple hand over a tree-like structure within the data attribute of our grid store. The major work of rendering the tree was handed over to our own implementation of a grid view:



Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

useArrows: true,

staticTree: false,

constructor: function(config) {
this.emptyIcon = Ext.BLANK_IMAGE_URL;

Ext.ux.tree.GridView.superclass.constructor.call(this, config);
this.templates = {};
this.templates.master = new Ext.Template(
'<div class="x-grid3 tq-treegrid" hidefocus="true">',
'<div class="x-grid3-viewport">',
'<div class="x-grid3-header">',
'<div class="x-grid3-header-inner">',
'<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
'</div>',
'<div class="x-clear"></div>',
'</div>',
'<div class="x-grid3-scroller">',
'<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
'<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
'</div>',
'</div>',
'<div class="x-grid3-resize-marker">&#160;</div>',
'<div class="x-grid3-resize-proxy">&#160;</div>',
'</div>'
);

this.templates.row = new Ext.Template(
'<div class="x-grid3-row {alt}" style="{tstyle}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody class="x-tree-node">',
'<tr>{cells}</tr>',
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
'</tbody>',
'</table>',
'</div>'
);
this.templates.treeCell = new Ext.Template(
'<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
'<div class="tq-treegrid-icons">',
'<span class="x-tree-node-indent">{nodeIndent}</span>',
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
'<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
'</div>',
'<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
'{value}',
'</div>',
'</div>',
'</td>'
);
},

getChildIndentUI: function(node) {
var indentBuffer = [];
var parentNode = node.parentNode;
while (parentNode){
if (!parentNode.isRoot){
if (!parentNode.isLast()) {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
} else {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
}
}
parentNode = parentNode.parentNode;
}
return indentBuffer.join("");
},

getTreeNodeIcon: function(node) {
var treeIcon = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
if (node.hasChildNodes() && !this.staticTree){
if (node.expanded){
treeIcon += "-minus";
} else {
treeIcon += "-plus";
}
treeIcon += ' tq-tree-node-control';
}
return treeIcon;
},

// private
doRender: function(cs, rs, ds, startRow, colCount, stripe){
// buffers
var rowBuffer = [];
var cellBuffer;
var cell;
var cellTemplate;
var cellProperties = {};
var rowProperties = {
tstyle: 'width:'+this.getTotalWidth()+';'
};
var record;
var depthBuffer = [];
var hasTreeCol;

for (var j = 0, len = rs.length; j < len; j++){
record = rs[j];
cellBuffer = [];
hasTreeCol = false;
var rowIndex = (j+startRow);


for (var i = 0; i < colCount; i++){
cell = cs[i];
cellProperties.id = cell.id;
cellProperties.css = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
cellProperties.attr = cellProperties.cellAttr = '';
cellProperties.value = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
cellProperties.style = cell.style;
if (Ext.isEmpty(cellProperties.value)){
cellProperties.value = '&#160;';
}
if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
cellProperties.css += ' x-grid3-dirty-cell';
}

if (cell.scope.treeCol && !hasTreeCol && record.node) {
hasTreeCol = true;
var node = record.node;

cellProperties.nodeIndent = this.getChildIndentUI(node);
cellProperties.nodeIconCls = node.attributes.iconCls || '';

cellProperties.nodeTreeIconCls = this.getTreeNodeIcon(node);

cellTemplate = this.templates.treeCell;
} else {
cellTemplate = this.templates.cell;
}
cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

}
var alt = [];

if (record.depth && record.node) {
if (depthBuffer.length < record.depth) {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}

if (this.staticTree) {
alt.push('tq-treegrid-static');
}

if (node.isLeaf()) {
alt.push('x-tree-node-leaf');
} else if (node.expanded){
alt.push('x-tree-node-expanded');
} else {
alt.push('x-tree-node-collapsed');
}
}

if(stripe && ((rowIndex+1) % 2 === 0)){
alt.push('x-grid3-row-alt');
}
if(record.dirty){
alt.push(' x-grid3-dirty-row');
}

rowProperties.cols = colCount;
rowProperties.nodeId = record.node.id;
rowProperties.cells = cellBuffer.join('');
if (this.getRowClass){
alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
}
rowProperties.alt = alt.join(' ');

rowBuffer[rowBuffer.length] = this.templates.row.apply(rowProperties);
}

while (depthBuffer.length) {
rowBuffer.push(depthBuffer.pop());
}
return rowBuffer.join('');
},

afterRender: function() {
Ext.ux.tree.GridView.superclass.afterRender.call(this);

if (!this.staticTree) {
this.mainBody.on('click', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.tq-tree-node-control'
});
this.mainBody.on('dblclick', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.x-tree-node-el '
});
}
},

toggleNode: function(el) {
var row = Ext.get(this.findRow(el));
var node = this.grid.getStore().getAt(row.dom.rowIndex).node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideOut('t', {
callback : function(){
row.highlight();
node.expanded = false;
},
scope: this,
duration: .25
});

} else {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideIn('t', {
callback : function(){
childCnt.highlight();
node.expanded = true;
},
scope: this,
duration: .25
});
}
}
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}

});


That's where the hard work is done (event though a decent amount of code had to be copied over from the original implementation). Be aware that we currently use CSS classes from the grid itself, from the tree and from the treegrid - that's also not really production ready outside our environment.
Together with some CSS additions we can easily set up our own tree grid:



/**
* Tree grid
*/
.tq-treegrid .tq-treegrid-col {
border: none;
}

.tq-treegrid .tq-treegrid-icons {
float: left;
}

.tq-treegrid .x-tree-node-el {
line-height: 13px;
padding: 1px 3px 1px 5px;
}

.tq-treegrid .tq-treegrid-static .x-tree-ec-icon {
display: none;
}

.tq-treegrid .tq-treegrid-static .x-tree-node-el {
cursor: default;
}




var treeGrid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: {
xtype: 'store',
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id',
type: 'int'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost',
type: 'float'
}]
}),
data: [{
id: 1,
name: 'Company A',
cost: 1000000.00,
leaf: false,
expanded: true,
children: [{
id: 11,
name: 'Department AA',
cost: 800000.00,
leaf: false,
expanded: true,
children: [{
id: 111,
name: 'Thing AAA',
cost: 300000.00,
leaf: true
}, {
id: 112,
name: 'Thing AAB',
cost: 500000.00,
leaf: true
}]
}, {
id: 12,
name: 'Department AB',
cost: 200000.00,
leaf: false,
expanded: true,
children: [{
id: 121,
name: 'Thing ABA',
cost: 50000.00,
leaf: true
}, {
id: 122,
name: 'Thing ABB',
cost: 150000.00,
leaf: true
}]
}]
}, {
id: 2,
name: 'Company B',
cost: 200000.00,
leaf: false,
expanded: true,
children: [{
id: 21,
name: 'Department BA',
cost: 100000.00,
leaf: false,
expanded: true,
children: [{
id: 211,
name: 'Thing BAA',
cost: 50000.00,
leaf: true
}, {
id: 212,
name: 'Thing BAB',
cost: 50000.00,
leaf: true
}]
}, {
id: 22,
name: 'Department BB',
cost: 100000.00,
leaf: false,
expanded: true,
children: [{
id: 221,
name: 'Thing BBA',
cost: 90000.00,
leaf: true
}, {
id: 222,
name: 'Thing BBB',
cost: 10000.00,
leaf: true
}]
}]
}]
},
columns: [{
header: 'Id',
dataIndex: 'id',
width: 30
}, {
header: 'Name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'Cost',
dataIndex: 'cost',
width: 80
}]
});


Fell free to use this base work at your will.
Best regards to all of you!

Stefan

Stefan Gehrig
TEQneers GmbH & Co. KG, Stuttgart, Germany

buenavida
13 Mar 2010, 9:36 AM
Hi Stefan,

Many thanks for this work.
I saw that the column with icons (collapsible), appears funny in IE 8. (the column entry is separated into 2 lines, one line for icon and the other for the description). Do you see the same thing in IE8?

Best Regards
Marty

teqneers
14 Mar 2010, 11:58 PM
Hi Marty,

I just checked with all available browsers (IE6, IE7, IE8, Opera, Firefox and Chrome) and the tree-column looks OK in all of those. Most likely you're missing some CSS rules that we borrowed from the Ext.ux.tree.TreeGrid. DO you have the same issue in other browsers?

Best regards

Stefan

Stefan Gehrig
TEQneers GmbH & Co. KG, Stuttgart, Germany

aldanco
15 Mar 2010, 2:16 PM
Work very fine in all browsers I've tried. Thank you so much for this wonderful work.

albeva
16 Mar 2010, 3:28 AM
hmm very nice work and integrates very seemlessly with different themes unlike the tree grid plugin shipped with Ext. I really like how this is setup. Only big problem is sorting

teqneers
16 Mar 2010, 3:42 AM
Hi albeva,

thanks a lot...
You're right about sorting. We currently don't need the grid's sorting abilities with our tree grid so I haven't looked into this topic yet (furthermore the code currently does not support adding, removing or reordering nodes yet). That's why I carefully called the code "not even production-ready". It should be considered a "proof-of-concept" for a "better" or more integrative implementation of a tree grid.

Best regards

Stefan

Stefan Gehrig
TEQneers GmbH & Co. KG, Stuttgart, Germany

masterix
8 Apr 2010, 8:41 AM
Hi teqneers,

i'm trying it, but don't work with a JsonStore. This is my code:



var store = new Ext.data.JsonStore({
url: 'json/data.php',
totalProperty: 'results',
idProperty: 'id',
root: 'rows',
autoLoad: true,
remoteSort: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id',
type: 'int'
}, {
name: 'name',
mapping: 'name'
}]
})
});
store.setDefaultSort('id', 'asc');

var treeGrid = new Ext.grid.GridPanel({
renderTo: Ext.getBody(),
width: 400,
height: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: store,
columns: [{
header: 'Id',
dataIndex: 'id',
width: 30
}, {
header: 'Name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}]
});


can you help me? thank you :)

jay@moduscreate.com
8 Apr 2010, 11:54 AM
Hi albeva,

thanks a lot...
You're right about sorting. We currently don't need the grid's sorting abilities with our tree grid so I haven't looked into this topic yet (furthermore the code currently does not support adding, removing or reordering nodes yet). That's why I carefully called the code "not even production-ready". It should be considered a "proof-of-concept" for a "better" or more integrative implementation of a tree grid.

Best regards

Stefan

Stefan Gehrig
TEQneers GmbH & Co. KG, Stuttgart, Germany

You don't need to continuously paste your signature. add it to your profile. http://www.extjs.com/forum/usercp.php

teqneers
8 Apr 2010, 10:28 PM
Nice idea... Thanks ;-)

teqneers
8 Apr 2010, 10:32 PM
Can you please post how your data, which is returned by json/data.php, looks?

It's most likely that the data is not correctly formatted (the TreeReader is not as sophisticated as the other readers yet, so it requires a quite strict data format).

Best regards

Stefan

ing_marco
10 Apr 2010, 6:08 PM
Can you please post how your data, which is returned by json/data.php, looks?

It's most likely that the data is not correctly formatted (the TreeReader is not as sophisticated as the other readers yet, so it requires a quite strict data format).

Best regards

Stefan

I have problems with data, when i use hardcode data, it work, but when i try using a Ext.data.JsonStore dont display data.
Here my code and data.


var store1 = new Ext.data.JsonStore({
xtype: 'store',

proxy: new Ext.data.ScriptTagProxy({
url: "http://localhost:1816/TaskManager/WebService/ScriptPageProxy.aspx/ObtenerProyectosTest",
method: 'POST',
timeout : 5000
}),
totalProperty: 'd.NroItems',
idProperty: 'id',
root: 'd.LstTask',
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
})

});
store1.setDefaultSort('id', 'asc');

//store1.load();


//alert('Records: ' + records[0].data.Mensaje);
var treeGrid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: store1,
columns: [{
header: 'id',
dataIndex: 'id',
width: 30
}, {
header: 'name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'cost',
dataIndex: 'cost',
width: 80
}]
});


store1.load();


data is like this:


stcCallback1001({ d: {"NroItems":1,"LstTask":[{"id":3,"name":"Task3","cost":1000,"Leaf":true,"Expanded":false,"children":[{"id":2,"name":"Task2","cost":1000,"Leaf":true,"Expanded":false,"children":[{"id":1,"name":"Task1","cost":1000,"Leaf":true,"Expanded":false,"children":null}]}]}]} });


plz help me.

ing_marco
10 Apr 2010, 6:11 PM
Can you please post how your data, which is returned by json/data.php, looks?

It's most likely that the data is not correctly formatted (the TreeReader is not as sophisticated as the other readers yet, so it requires a quite strict data format).

Best regards

Stefan

I have problems with data, when i use hardcode data, it work, but when i try using a Ext.data.JsonStore dont display data.
Here my code and data.


var store1 = new Ext.data.JsonStore({
xtype: 'store',

proxy: new Ext.data.ScriptTagProxy({
url: "http://localhost:1816/TaskManager/WebService/ScriptPageProxy.aspx/ObtenerProyectosTest",
method: 'POST',
timeout : 5000
}),
totalProperty: 'd.NroItems',
idProperty: 'id',
root: 'd.LstTask',
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
})

});
store1.setDefaultSort('id', 'asc');

//store1.load();


//alert('Records: ' + records[0].data.Mensaje);
var treeGrid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: store1,
columns: [{
header: 'id',
dataIndex: 'id',
width: 30
}, {
header: 'name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'cost',
dataIndex: 'cost',
width: 80
}]
});


store1.load();


data is like this:


stcCallback1001({ d: {"NroItems":1,"LstTask":[{"id":3,"name":"Task3","cost":1000,"Leaf":true,"Expanded":false,"children":[{"id":2,"name":"Task2","cost":1000,"Leaf":true,"Expanded":false,"children":[{"id":1,"name":"Task1","cost":1000,"Leaf":true,"Expanded":false,"children":null}]}]}]} });


plz help me.

teqneers
11 Apr 2010, 10:44 AM
The problem is, that the TreeReader does not yet support neither totalProperty nor root (by the way both configuration directives are part of JsonReader not of JsonStore originally - they are just transparently moved from the JsonStore into the JsonReader), so your data should be structured like that:



[{
"id": 3,
"name": "Task3",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": [{
"id": 2,
"name": "Task2",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": [{
"id": 1,
"name": "Task1",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": null
}]
}]
}]


Note the missing root property and that the first level tree nodes start directly witn a first-level array.

Best regards

Stefan

ing_marco
12 Apr 2010, 9:11 AM
The problem is, that the TreeReader does not yet support neither totalProperty nor root (by the way both configuration directives are part of JsonReader not of JsonStore originally - they are just transparently moved from the JsonStore into the JsonReader), so your data should be structured like that:



[{
"id": 3,
"name": "Task3",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": [{
"id": 2,
"name": "Task2",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": [{
"id": 1,
"name": "Task1",
"cost": 1000,
"Leaf": true,
"Expanded": false,
"children": null
}]
}]
}]


Note the missing root property and that the first level tree nodes start directly witn a first-level array.

Best regards

Stefan
I make changes but i cant get work it.
Can you tell me all .js reference i need for this example?
But i think my error is with Ext.ux.tree.TreeReader

My data have this format:

[{'id': 3,'name': 'Task3','cost': 1000,'leaf': true,'expanded': false,'children': [{'id': 2,'name': 'Task2','cost': 1000,'leaf': true,'expanded': false,'children': [{'id': 1,'name': 'Task1','cost': 1000,'leaf': true,'expanded': false,'children': null}]}]}]

trying i forget put TreeReader and get 1 row, but when i put TreeReader, dont show any row.

Have any idea about my problem?

I forget:
Im using a aspx, to return data, i see with firebug that data is correct.
Maybe you have any example when use aspx Pages and your Grid-Tree.
Plz y need fit my error.

teqneers
13 Apr 2010, 2:03 AM
First of all, please make sure that the "leaf" attribute is set correctly according to the number of children a node has (no children = "leaf = true"). Then it's "leaf" not "Lead" and "expanded" not "Expanded".

To eliminate any possible problems with the proxy, please try and put the JSON-formatted data directly into the "data" attribute of your store - and do not use a JSON store but a plain store instead (and no not mix xtype with direct instantiation).



var store1 = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),
data: [{
"id": 3,
"name": "Task3",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 2,
"name": "Task2",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 1,
"name": "Task1",
"cost": 1000,
"leaf": true,
"expanded": false
}]
}]
}]
});


Have you tried to run the example I provides in the original post? If so and if it works, then try to incrementally add features such as loading the data through a proxy.

Best regards

Stefan

Tim Toady
14 Apr 2010, 1:10 PM
Has anyone tried putting an editor: new Ext.form.TextField() in the tree column?
I have tried this and it works to edit, but toggling collapse and expand stops working for that row after it is edited. Any ideas? Thanks

(Just a side note, I have it to still toggle on doubleclick. I set a variable and check in the beforeedit listener so that it will only work if a toolbar button for edit is clicked)

Tim Toady
15 Apr 2010, 4:48 AM
I looked into the issue again and noticed and extra <div class="x-tree-node-ct"> gets stuck around the row when it gets marked dirty. To fix the problem I changed line 145 which is part of the doRender function.

if (depthBuffer.length < record.depth && !record.dirty) {

teqneers
15 Apr 2010, 5:03 AM
Hi Tim,

we also noticed this issue when rows are getting marked as "dirty". This happens because the Ext grid re-renders the dirty row and the doRender function always tries to insert the hierarchy divs no matter if it does a full or a partial rendering. The way we fixed this issue was to change the same row to:



if (startRow == 0 && depthBuffer.length < record.depth) {


Best regards

Stefan

Tim Toady
15 Apr 2010, 6:06 AM
Before I go and try to implement it. Have you created a way to append a child node to a node? (Insert a record sort of thing)

teqneers
15 Apr 2010, 7:56 AM
Hi Tim,

no - as we don't currently need to manipulate the structure of the displayed data on the fly, I haven't looked into reordering, adding and removing nodes (or rows in the grid context).
As I said somewhere above: we did not have the intention to publish a feature-complete component but rather give an example of how such a component can be started. Current development is always directly linked to our application specific needs.

Sorry - but I'd greatly interested if you try to add tree-manipulation features to the grid.

Best regards

Stefan

Tim Toady
15 Apr 2010, 12:45 PM
I am 99% done with being able to append a child. However I am not sure if it is buggy or if the treegridview is buggy since I can't really define the problem. I am leaning towards it being a problem with my code. It works fine until rows are marked dirty and a child is appended to a parent of the dirty row. The problem is more complex than that though. I can't replicate the issue every time or without playing with it.

Here is my code. It is just on a button. Rec is a new record, rowNum the selected row. I am just using refresh() at the end for the time being. I am aware it should just be refreshing the particular rows being changed. I will pull it out and make some sort of function once I can get it working correctly.

Please let me know if you can think of what is causing my problem. The HTML that gets rendered gets messed up and edited folders dont get collapsed.

(By the way, I had to add node.record = record; to the cascade in the treeGridReader)



rec.node = grid.store.reader.createNode({expanded: false});
rec.node.record = rec;
rec.node.leaf = true;
var pnode = grid.store.getAt(rowNum).node;
rec.depth = pnode.getDepth()+1;

pGetLastDescendant = function(node) {
if(node.childNodes.length > 0){
node = pGetLastDescendant(node.lastChild);
}
return node;
};
insertIndex = grid.store.indexOf(pGetLastDescendant(pnode).record) + 1;

pnode.appendChild(rec.node);
pnode.leaf = false;
pnode.expanded = true;
pnode.record.data.leaf = false;
rec.data.leaf = true;

grid.store.insert(insertIndex,rec);
grid.getView().refresh();

Tim Toady
16 Apr 2010, 5:39 AM
I have attached 4 images showing screenshots of what is rendered.

01 - before I add any rows. http://tinypic.com/r/mwsetx/5
02 - I add a child to ctest. Everything looks fine. http://tinypic.com/r/ffbqsx/5
03 - I edit the child I just added. Still looks good. http://tinypic.com/r/vsp2yv/5
04 - I add a child to bTest. HTML looks messed up. Ctest wont collapse http://tinypic.com/r/2m68z7b/5

I am assuming the order of the records matter when I insert the child. Perhaps I am putting the record in the wrong spot? What I am doing is putting the new record after the last descendant of the last child. (Since it appears visually after those I thought that is where it is supposed to go). Any ideas what I should do to fix it? Or is it something in the dorender?

Edit--------------
For some reason they got scaled too small. See urls for images


Edit2----------
I did it again without editing it until after I added test and testfgs has the class x-tree-node-ct and it works properly. The div it has in 04 is the child of the x-tree-node-ct when it is working. I am not sure why it loses it when the edit is done before the last add.

Tim Toady
16 Apr 2010, 8:19 AM
Sometimes walking away for a little while and coming back to a problem later is all that is needed. I am not sure why I didn't think of it sooner. It just popped into my head. My problem is caused by the way I fixed the bug on line 145 of the treegridview. So instead I used your way and it solved the problem. The way you posted still breaks on the first row if it is edited though (regardless of adding a child, it is the same edit bug as earlier).

digant
7 Jun 2010, 7:33 AM
Has anyone tried putting an editor: new Ext.form.TextField() in the tree column?
I have tried this and it works to edit, but toggling collapse and expand stops working for that row after it is edited. Any ideas? Thanks

(Just a side note, I have it to still toggle on doubleclick. I set a variable and check in the beforeedit listener so that it will only work if a toolbar button for edit is clicked)

I am pretty new to extjs and so I am struggling to make the leaf editable. Please help.

Tim Toady
9 Jun 2010, 6:53 AM
It depends. In most cases you just have to add an editor to the column model. For my treegrid I added a textfield as my editor.
The treegrid is a bit more difficult though since it already has click actions. What I did was use a button on my toolbar that would edit the selected record. Here is how I did it

This is in my button handler


grid = this.ownerCt.ownerCt.items.items[0];
rowSm = grid.getSelectionModel();
rowNum = rowSm.selection.cell[0];

grid.doEdit = true;
grid.startEditing(rowNum, 0); And this was in my treegrid


doEdit: false,
listeners: {
beforeedit: function(object) {
return (this.doEdit);
},
afteredit: function(object) {
this.doEdit = false;
}
}I wrote this a while back so there may be more to it than that and it may not be the best way, but it works for me. You could also try to override it in some way to do it with some sort of click action but I did it with a tooldar button instead.

There is still that bug on the first row being edit messes up the HTML. I was hoping someone else would fix it =/ I haven't had the time to try again. Its just a matter of writing the correct If statement but I dont have a full understanding of the treegridview and don't have hours on end to study it to write an If statement

puran4singh
11 Jun 2010, 6:31 AM
I am getting problem on loading treegrid from XMl,which is having Root Id(id of node) and Parent_id (id of any node who's child it is)

A is parent of B then <A RID=1> and <B RID=2 PID=1>
it is flat xml.

treblereel
13 Jun 2010, 12:43 PM
Hi all, cant find our Whats wrong with AJAXstore ? If i use 'store' i get empty panel, but with 'store1' everythings ok!
here my code:



<html>
<head>
<title>A</title>

<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>js/extjs/resources/css/ext-all.css"></link>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/ext-all.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/adapter/ext/exp.js"></script>



</head>
<style type="text/css">

.tq-treegrid .tq-treegrid-col {
border: none;
}

.tq-treegrid .tq-treegrid-icons {
float: left;
}

.tq-treegrid .x-tree-node-el {
line-height: 13px;
padding: 1px 3px 1px 5px;
}

.tq-treegrid .tq-treegrid-static .x-tree-ec-icon {
display: none;
}

.tq-treegrid .tq-treegrid-static .x-tree-node-el {
cursor: default;
}


</style>

<body style="padding: 50px;"></body>

<script type="text/javascript">


Ext.onReady(function() {
Ext.QuickTips.init();


var store = new Ext.data.JsonStore({
url: '<?php echo base_url();?>hour/hour_json',
method: 'POST',
autoLoad: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
},{
name: 'cost',
mapping: 'cost'
}]
})
});
store.setDefaultSort('id', 'asc');
var store1 = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),
data: [{
"id": 3,
"name": "Task3",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 2,
"name": "Task2",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 1,
"name": "Task1",
"cost": 1000,
"leaf": true,
"expanded": false
}]
}]
}]
});
var treeGrid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: store,
columns: [{
header: 'Id',
dataIndex: 'id',
width: 30
}, {
header: 'Name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'Cost',
dataIndex: 'cost',
width: 80
}]
});
});
</script>
</html>




here is my json output, the same like 'store'


[{"id":3,"name":"Task3","cost":1000,"leaf":false,"expanded":false,"children":[{"id":2,"name":"Task2","cost":1000,"leaf":false,"expanded":false,"children":[{"id":1,"name":"Task1","cost":1000,"leaf":true,"expanded":false}]}]}]


here is ext.ux.tree:



Ext.ns('Ext.ux.tree');


Ext.ux.tree.TreeReader = Ext.extend(Ext.data.DataReader, {

tree: null,

constructor: function(meta, recordType) {
Ext.ux.tree.TreeReader.superclass.constructor.call(this, meta, recordType || meta.fields);
},

load: function(node){
if (node.attributes.children){
var cs = node.attributes.children;
for (var i = 0, len = cs.length; i < len; i++){
var cn = node.appendChild(this.createNode(cs[i]));
this.load(cn);
}
}
},

createNode: function(attr){
var node = new Ext.data.Node(attr);
node.expanded = (attr.expanded === true);
return node;
},

/**
* Create a data block containing Ext.data.Records from a tree.
*/
readRecords: function(o) {

var root = this.createNode({
text: 'Root',
id: 'root',
children: o
});
this.tree = new Ext.data.Tree(root);
this.load(root);

var f = this.recordType.prototype.fields;
var records = [];
root.cascade(function(node) {
if (node !== root) {
var record = new this.recordType(this.extractValues(node, f.items), node.id);
record.node = node;
record.depth = node.getDepth();
records.push(record);
}
}, this);

return {
success : true,
records : records,
totalRecords : records.length
};
},

/**
* type-casts a single node
*/
extractValues : function(node, fields) {
var f, values = {};
for(var j = 0; j < fields.length; j++){
f = fields[j];
var v = node.attributes[f.mapping];
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, node);
}
return values;
}
});


Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

useArrows: true,

staticTree: false,

constructor: function(config) {
this.emptyIcon = Ext.BLANK_IMAGE_URL;

Ext.ux.tree.GridView.superclass.constructor.call(this, config);
this.templates = {};
this.templates.master = new Ext.Template(
'<div class="x-grid3 tq-treegrid" hidefocus="true">',
'<div class="x-grid3-viewport">',
'<div class="x-grid3-header">',
'<div class="x-grid3-header-inner">',
'<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
'</div>',
'<div class="x-clear"></div>',
'</div>',
'<div class="x-grid3-scroller">',
'<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
'<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
'</div>',
'</div>',
'<div class="x-grid3-resize-marker"> </div>',
'<div class="x-grid3-resize-proxy"> </div>',
'</div>'
);

this.templates.row = new Ext.Template(
'<div class="x-grid3-row {alt}" style="{tstyle}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody class="x-tree-node">',
'<tr>{cells}</tr>',
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
'</tbody>',
'</table>',
'</div>'
);
this.templates.treeCell = new Ext.Template(
'<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
'<div class="tq-treegrid-icons">',
'<span class="x-tree-node-indent">{nodeIndent}</span>',
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
'<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
'</div>',
'<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
'{value}',
'</div>',
'</div>',
'</td>'
);
},

getChildIndentUI: function(node) {
var indentBuffer = [];
var parentNode = node.parentNode;
while (parentNode){
if (!parentNode.isRoot){
if (!parentNode.isLast()) {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
} else {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
}
}
parentNode = parentNode.parentNode;
}
return indentBuffer.join("");
},

getTreeNodeIcon: function(node) {
var treeIcon = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
if (node.hasChildNodes() && !this.staticTree){
if (node.expanded){
treeIcon += "-minus";
} else {
treeIcon += "-plus";
}
treeIcon += ' tq-tree-node-control';
}
return treeIcon;
},

// private
doRender: function(cs, rs, ds, startRow, colCount, stripe){
// buffers
var rowBuffer = [];
var cellBuffer;
var cell;
var cellTemplate;
var cellProperties = {};
var rowProperties = {
tstyle: 'width:'+this.getTotalWidth()+';'
};
var record;
var depthBuffer = [];
var hasTreeCol;

for (var j = 0, len = rs.length; j < len; j++){
record = rs[j];
cellBuffer = [];
hasTreeCol = false;
var rowIndex = (j+startRow);


for (var i = 0; i < colCount; i++){
cell = cs[i];
cellProperties.id = cell.id;
cellProperties.css = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
cellProperties.attr = cellProperties.cellAttr = '';
cellProperties.value = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
cellProperties.style = cell.style;
if (Ext.isEmpty(cellProperties.value)){
cellProperties.value = ' ';
}
if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
cellProperties.css += ' x-grid3-dirty-cell';
}

if (cell.scope.treeCol && !hasTreeCol && record.node) {
hasTreeCol = true;
var node = record.node;

cellProperties.nodeIndent = this.getChildIndentUI(node);
cellProperties.nodeIconCls = node.attributes.iconCls || '';

cellProperties.nodeTreeIconCls = this.getTreeNodeIcon(node);

cellTemplate = this.templates.treeCell;
} else {
cellTemplate = this.templates.cell;
}
cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

}
var alt = [];

if (record.depth && record.node) {
if (depthBuffer.length < record.depth) {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}

if (this.staticTree) {
alt.push('tq-treegrid-static');
}

if (node.isLeaf()) {
alt.push('x-tree-node-leaf');
} else if (node.expanded){
alt.push('x-tree-node-expanded');
} else {
alt.push('x-tree-node-collapsed');
}
}

if(stripe && ((rowIndex+1) % 2 === 0)){
alt.push('x-grid3-row-alt');
}
if(record.dirty){
alt.push(' x-grid3-dirty-row');
}

rowProperties.cols = colCount;
rowProperties.nodeId = record.node.id;
rowProperties.cells = cellBuffer.join('');
if (this.getRowClass){
alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
}
rowProperties.alt = alt.join(' ');

rowBuffer[rowBuffer.length] = this.templates.row.apply(rowProperties);
}

while (depthBuffer.length) {
rowBuffer.push(depthBuffer.pop());
}
return rowBuffer.join('');
},

afterRender: function() {
Ext.ux.tree.GridView.superclass.afterRender.call(this);

if (!this.staticTree) {
this.mainBody.on('click', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.tq-tree-node-control'
});
this.mainBody.on('dblclick', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.x-tree-node-el '
});
}
},

toggleNode: function(el) {
var row = Ext.get(this.findRow(el));
var node = this.grid.getStore().getAt(row.dom.rowIndex).node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideOut('t', {
callback : function(){
row.highlight();
node.expanded = false;
},
scope: this,
duration: .25
});

} else {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideIn('t', {
callback : function(){
childCnt.highlight();
node.expanded = true;
},
scope: this,
duration: .25
});
}
}
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}

});




Plz, help i really need this !

teqneers
14 Jun 2010, 3:07 AM
You cannot use a Ext.data.JsonStore as an Ext.data.JsonStore automatically gets configured with an Ext.data.JsonReader which in turn cannot handle the data structure required by the tree grid. That's why you have to restort to a plain Ext.data.Store configured with an Ext.ux.tree.TreeReader and appropriate Ext.data.DataProxy, e.g. an Ext.data.HttpProxy which is the default proxy to be used when you simply provide a url to the data store. So, this should work:


var store = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),
url: '<?php echo base_url();?>hour/hour_json',
method: 'POST'
});

treblereel
14 Jun 2010, 3:53 AM
hmm, strange ... in config Method: Post, but in firebug GET.

and i still have empty panel, i check JSON answer twice and it should work !



[{"id":3,"name":"Task3","cost":1000,"leaf":false,"expanded":false,"children":[{"id":2,"name":"Task2","cost":1000,"leaf":false,"expanded":false,"children":[{"id":1,"name":"Task1","cost":1000,"leaf":true,"expanded":false}]}]}]


so this works !


var store = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),
data: [{
"id": 3,
"name": "Task3",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 2,
"name": "Task2",
"cost": 1000,
"leaf": false,
"expanded": false,
"children": [{
"id": 1,
"name": "Task1",
"cost": 1000,
"leaf": true,
"expanded": false
}]
}]
}]
// url: '<?php echo base_url();?>hour/hour_json',
// method: 'POST'
});


this nope:



var store = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),
// data: [{
// "id": 3,
// "name": "Task3",
// "cost": 1000,
// "leaf": false,
// "expanded": false,
// "children": [{
// "id": 2,
// "name": "Task2",
// "cost": 1000,
// "leaf": false,
// "expanded": false,
// "children": [{
// "id": 1,
// "name": "Task1",
// "cost": 1000,
// "leaf": true,
// "expanded": false
// }]
// }]
// }]
url: '<?php echo base_url();?>hour/hour_json',
method: 'POST'
});



how do i can check that store is get data ? something like console.log fro arrays ?

Thanks!

treblereel
14 Jun 2010, 5:09 AM
mode details:
with httpProxy


var store1 = new Ext.data.Store({
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),

proxy: new Ext.data.HttpProxy({
url: '<?php echo base_url();?>hour/hour_json',
method: 'POST'
}),

});

store1.load();
console.dir(store1);



with console.dir i find what store1 is empty, looks like Ext.ux.tree.TreeReader cant parse json data.
i have past json data before on this topic

Plz help

teqneers
14 Jun 2010, 9:22 AM
Please check the data that's coming back from your actual request using FireBug or some other tool that may allow you to introspect the HTTP communication. There is a lot that can go wrong. My initial tip would be to check if a Ext.data.Store can be configured with a method-parameter.
But generally you should first check if there's a request being fired, what it contains and what your server returns in response.

treblereel
14 Jun 2010, 9:49 AM
Thanks for your answer, i am trying change your example to use Json. From server side my JSON answer is:



[{"id":3,"name":"Task3","cost":1000,"leaf":false,"expanded":false,"children":[{"id":2,"name":"Task2","cost":1000,"leaf":false,"expanded":false,"children":[{"id":1,"name":"Task1","cost":1000,"leaf":true,"expanded":false}]}]}]



its just a copy from your example ...

here is my full code:




<html>
<head>
<title></title>

<link rel="stylesheet" type="text/css" href="<?php echo base_url();?>js/extjs/resources/css/ext-all.css"></link>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/ext-all.js"></script>
<script type="text/javascript" src="<?php echo base_url();?>js/extjs/adapter/ext/exp.js"></script>



</head>


<body style="padding: 50px;"></body>

<script type="text/javascript">


Ext.onReady(function() {
Ext.QuickTips.init();


var store = new Ext.data.Store({
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'cost',
mapping: 'cost'
}]
}),

proxy: new Ext.data.HttpProxy({
url: '<?php echo base_url();?>hour/hour_json',
method: 'POST'
}),

});

store.load();


var treeGrid = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: store,
columns: [{
header: 'Id',
dataIndex: 'id',
width: 30
}, {
header: 'Name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'Cost',
dataIndex: 'cost',
width: 80
}]
});
});
</script>
</html>


exp.js containts your code (Ext.ux.tree.TreeReader)

may it be that i have problems coz i set namespace ?



Ext.ns('Ext.ux.tree');


Ext.ux.tree.TreeReader = Ext.extend(Ext.data.DataReader, {

tree: null,

constructor: function(meta, recordType) {
Ext.ux.tree.TreeReader.superclass.constructor.call(this, meta, recordType || meta.fields);
},

load: function(node){
if (node.attributes.children){
var cs = node.attributes.children;
for (var i = 0, len = cs.length; i < len; i++){
var cn = node.appendChild(this.createNode(cs[i]));
this.load(cn);
}
}
},

createNode: function(attr){
var node = new Ext.data.Node(attr);
node.expanded = (attr.expanded === true);
return node;
},

/**
* Create a data block containing Ext.data.Records from a tree.
*/
readRecords: function(o) {

var root = this.createNode({
text: 'Root',
id: 'root',
children: o
});
this.tree = new Ext.data.Tree(root);
this.load(root);

var f = this.recordType.prototype.fields;
var records = [];
root.cascade(function(node) {
if (node !== root) {
var record = new this.recordType(this.extractValues(node, f.items), node.id);
record.node = node;
record.depth = node.getDepth();
records.push(record);
}
}, this);

return {
success : true,
records : records,
totalRecords : records.length
};
},

/**
* type-casts a single node
*/
extractValues : function(node, fields) {
var f, values = {};
for(var j = 0; j < fields.length; j++){
f = fields[j];
var v = node.attributes[f.mapping];
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, node);
}
return values;
}
});


Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

useArrows: true,

staticTree: false,

constructor: function(config) {
this.emptyIcon = Ext.BLANK_IMAGE_URL;

Ext.ux.tree.GridView.superclass.constructor.call(this, config);
this.templates = {};
this.templates.master = new Ext.Template(
'<div class="x-grid3 tq-treegrid" hidefocus="true">',
'<div class="x-grid3-viewport">',
'<div class="x-grid3-header">',
'<div class="x-grid3-header-inner">',
'<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
'</div>',
'<div class="x-clear"></div>',
'</div>',
'<div class="x-grid3-scroller">',
'<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
'<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
'</div>',
'</div>',
'<div class="x-grid3-resize-marker"> </div>',
'<div class="x-grid3-resize-proxy"> </div>',
'</div>'
);

this.templates.row = new Ext.Template(
'<div class="x-grid3-row {alt}" style="{tstyle}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody class="x-tree-node">',
'<tr>{cells}</tr>',
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
'</tbody>',
'</table>',
'</div>'
);
this.templates.treeCell = new Ext.Template(
'<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
'<div class="tq-treegrid-icons">',
'<span class="x-tree-node-indent">{nodeIndent}</span>',
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
'<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
'</div>',
'<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
'{value}',
'</div>',
'</div>',
'</td>'
);
},

getChildIndentUI: function(node) {
var indentBuffer = [];
var parentNode = node.parentNode;
while (parentNode){
if (!parentNode.isRoot){
if (!parentNode.isLast()) {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
} else {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
}
}
parentNode = parentNode.parentNode;
}
return indentBuffer.join("");
},

getTreeNodeIcon: function(node) {
var treeIcon = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
if (node.hasChildNodes() && !this.staticTree){
if (node.expanded){
treeIcon += "-minus";
} else {
treeIcon += "-plus";
}
treeIcon += ' tq-tree-node-control';
}
return treeIcon;
},

// private
doRender: function(cs, rs, ds, startRow, colCount, stripe){
// buffers
var rowBuffer = [];
var cellBuffer;
var cell;
var cellTemplate;
var cellProperties = {};
var rowProperties = {
tstyle: 'width:'+this.getTotalWidth()+';'
};
var record;
var depthBuffer = [];
var hasTreeCol;

for (var j = 0, len = rs.length; j < len; j++){
record = rs[j];
cellBuffer = [];
hasTreeCol = false;
var rowIndex = (j+startRow);


for (var i = 0; i < colCount; i++){
cell = cs[i];
cellProperties.id = cell.id;
cellProperties.css = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
cellProperties.attr = cellProperties.cellAttr = '';
cellProperties.value = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
cellProperties.style = cell.style;
if (Ext.isEmpty(cellProperties.value)){
cellProperties.value = ' ';
}
if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
cellProperties.css += ' x-grid3-dirty-cell';
}

if (cell.scope.treeCol && !hasTreeCol && record.node) {
hasTreeCol = true;
var node = record.node;

cellProperties.nodeIndent = this.getChildIndentUI(node);
cellProperties.nodeIconCls = node.attributes.iconCls || '';

cellProperties.nodeTreeIconCls = this.getTreeNodeIcon(node);

cellTemplate = this.templates.treeCell;
} else {
cellTemplate = this.templates.cell;
}
cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

}
var alt = [];

if (record.depth && record.node) {
if (depthBuffer.length < record.depth) {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}

if (this.staticTree) {
alt.push('tq-treegrid-static');
}

if (node.isLeaf()) {
alt.push('x-tree-node-leaf');
} else if (node.expanded){
alt.push('x-tree-node-expanded');
} else {
alt.push('x-tree-node-collapsed');
}
}

if(stripe && ((rowIndex+1) % 2 === 0)){
alt.push('x-grid3-row-alt');
}
if(record.dirty){
alt.push(' x-grid3-dirty-row');
}

rowProperties.cols = colCount;
rowProperties.nodeId = record.node.id;
rowProperties.cells = cellBuffer.join('');
if (this.getRowClass){
alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
}
rowProperties.alt = alt.join(' ');

rowBuffer[rowBuffer.length] = this.templates.row.apply(rowProperties);
}

while (depthBuffer.length) {
rowBuffer.push(depthBuffer.pop());
}
return rowBuffer.join('');
},

afterRender: function() {
Ext.ux.tree.GridView.superclass.afterRender.call(this);

if (!this.staticTree) {
this.mainBody.on('click', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.tq-tree-node-control'
});
this.mainBody.on('dblclick', function(ev, el) {
this.toggleNode(el);
}, this, {
delegate: '.x-tree-node-el '
});
}
},

toggleNode: function(el) {
var row = Ext.get(this.findRow(el));
var node = this.grid.getStore().getAt(row.dom.rowIndex).node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideOut('t', {
callback : function(){
row.highlight();
node.expanded = false;
},
scope: this,
duration: .25
});

} else {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

childCnt.slideIn('t', {
callback : function(){
childCnt.highlight();
node.expanded = true;
},
scope: this,
duration: .25
});
}
}
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}

});





i have spend 2 last days trying to setup it, and i have no idea what wrong :-(

teqneers
14 Jun 2010, 11:44 PM
Your code looks good actually. It's quite hard to debug without any error messages or something like that.
Could you please add a callback to your store.load(); call to see, what's actually received by the store?


store.load({
callback: function() {
console.log(arguments);
}
});

This should print out the records received and extracted from the tree (1st argument), the options passed with the load operation (2nd argument) and the success-flag (3rd argument).

treblereel
14 Jun 2010, 11:53 PM
sure !

treblereel
15 Jun 2010, 12:34 AM
here is example: http://exp.lanstat.ru/index.html

royle78
16 Jun 2010, 1:48 PM
For some reason, when I specify 'expanded: false' on a node with children, it ignores it. Is there something I'm doing wrong? I see in the code that it's doing something w/ that option but it doesn't seem to work. The arrow and folder icon indicate that it's closed the but the container for that node's children is visible. Any suggestions? Is this happening to anyone else?

Thank you tequneers for doing this...this is exactly what I was looking for. I completely agree that the treegrid should have been done this way.

treblereel
18 Jun 2010, 3:13 AM
http://www.max-bazhenov.com/dev/ux.maximgb.tg/

jratcliff
7 Jul 2010, 1:27 PM
For some reason, when I specify 'expanded: false' on a node with children, it ignores it. Is there something I'm doing wrong? I see in the code that it's doing something w/ that option but it doesn't seem to work. The arrow and folder icon indicate that it's closed the but the container for that node's children is visible. Any suggestions? Is this happening to anyone else?

Thank you tequneers for doing this...this is exactly what I was looking for. I completely agree that the treegrid should have been done this way.

I had the same issue and I fixed it by modifying the doRender method:

BEFORE


if (startRow == 0 && depthBuffer.length < record.depth) {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}


AFTER


if (startRow == 0 && depthBuffer.length < record.depth) {
if (!node.parentNode.isRoot) {
if (node.parentNode.expanded) {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
} else {
rowBuffer.push('<div class="x-tree-node-ct" style="display: none;">');
depthBuffer.push('</div>');
}
} else {
rowBuffer.push('<div class="x-tree-node-ct">');
depthBuffer.push('</div>');
}
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}


basically, all I'm doing is checking to see if a parentNode is expanded and if it isn't, I set the display to none for the div that contains the children. Seems to work for me but I just have a simple tree that only goes one level deep. I haven't tested it on deeper trees.

Jack

mm_202
1 Aug 2010, 12:16 PM
It may be a little late, but for those that were having problems getting this to work with a Json-like store, I finally got it to working correctly.
The reason for the problem is that Ext.ux.tree.TreeReader is missing a read() function that Ext.data.HttpProxy expects to be there.

Just add the code below to Ext.ux.tree.TreeReader:

read: function(response) {
var json = response.responseText;
var o = Ext.decode(json);
if(!o) {
throw {message: 'JsonReader.read: Json object not found'};
}
return this.readRecords(o);
}

After that, all you need is a normal store with Ext.ux.tree.TreeReader:

var store = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [
{name:'id', mapping:'id'},
{name:'name', mapping:'name'},
{name:'cost', mapping:'cost'}
]
}),
url: 'ajax.php?testtree'
});

teqneers
2 Aug 2010, 9:17 AM
That seems to be correct... We've only used the component with static data or with an Ext.data.DirectProxy yet - and latter interfaces with the reader via the readRecords()-method. Strange! Normally you'd assume that components communicate via distinct interfaces.
But nice tip though - we'll integrate the missing read()-method in our code as well.

Thanks and best regards

Stefan

mm_202
2 Aug 2010, 3:25 PM
Does anyone have any quick solutions for collapsing/expanding individual nodes or checking to see if they are collapsed?

teqneers
2 Aug 2010, 11:25 PM
After having used the components on several occasions and after having read all the posts here, we further worked on the components and I'll just share the updated source here:


Ext.ux.tree.TreeReader = Ext.extend(Ext.data.DataReader, {

tree: null,

jsonData: null,

constructor: function(meta, recordType) {
Ext.ux.tree.TreeReader.superclass.constructor.call(this, meta, recordType || meta.fields);
},

load: function(node){
if (node.attributes.children){
var cs = node.attributes.children;
for (var i = 0, len = cs.length; i < len; i++){
if (cs[i]) {
var cn = node.appendChild(this.createNode(cs[i]));
this.load(cn);
}
}
}
},

createNode: function(attr){
var node = new Ext.data.Node(attr);
node.expanded = (attr.expanded === true);
return node;
},

read: function(response) {
var json = response.responseText;
var o = Ext.decode(json);
if (!o) {
throw { message: 'JsonReader.read: Json object not found' };
}
return this.readRecords(o);
},

/**
* Create a data block containing Ext.data.Records from a tree.
*/
readRecords: function(o) {
this.jsonData = o;

var items = o;
if (this.meta.root && o[this.meta.root] ) {
items = o[this.meta.root];
if (o.metaData) {
this.onMetaChange(o.metaData);
}
}

var root = this.createNode({
text: 'Root',
id: 'root',
children: items
});
this.tree = new Ext.data.Tree(root);
this.load(root);

var f = this.recordType.prototype.fields;
var records = [];
root.cascade(function(node) {
if (node !== root) {
var record = new this.recordType(this.extractValues(node, f.items), node.id);
record.node = node;
record.depth = node.getDepth();
records.push(record);
}
}, this);

return {
success : true,
records : records,
totalRecords : records.length
};
},


onMetaChange : function(meta){
Ext.apply(this.meta, meta);
},

/**
* type-casts a single node
*/
extractValues : function(node, fields) {
var f, m, values = {};
for(var j = 0; j < fields.length; j++){
f = fields[j];
m = f.mapping || f.name;
var v = node.attributes[m];
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, node);
}
return values;
}
});


Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

useArrows: true,

staticTree: false,

animate: false,

constructor: function(config) {
this.emptyIcon = Ext.BLANK_IMAGE_URL;

Ext.ux.tree.GridView.superclass.constructor.call(this, config);
this.templates = {};
this.templates.master = new Ext.Template(
'<div class="x-grid3 tq-treegrid" hidefocus="true">',
'<div class="x-grid3-viewport">',
'<div class="x-grid3-header">',
'<div class="x-grid3-header-inner">',
'<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
'</div>',
'<div class="x-clear"></div>',
'</div>',
'<div class="x-grid3-scroller">',
'<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
'<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
'</div>',
'</div>',
'<div class="x-grid3-resize-marker"> </div>',
'<div class="x-grid3-resize-proxy"> </div>',
'</div>'
);

this.templates.row = new Ext.Template(
'<div class="x-grid3-row {alt}" style="{tstyle}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody class="x-tree-node">',
'<tr>{cells}</tr>',
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
'</tbody>',
'</table>',
'</div>'
);
this.templates.treeCell = new Ext.Template(
'<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
'<div class="tq-treegrid-icons">',
'<span class="x-tree-node-indent">{nodeIndent}</span>',
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
'<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
'</div>',
'<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
'{value}',
'</div>',
'</div>',
'</td>'
);

this.addEvents('beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode');
},

getChildIndentUI: function(node) {
var indentBuffer = [];
var parentNode = node.parentNode;
while (parentNode){
if (!parentNode.isRoot){
if (!parentNode.isLast()) {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
} else {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
}
}
parentNode = parentNode.parentNode;
}
return indentBuffer.join("");
},

getTreeNodeIcon: function(node) {
var treeIcon = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
if (node.hasChildNodes() && !this.staticTree){
if (node.expanded){
treeIcon += "-minus";
} else {
treeIcon += "-plus";
}
treeIcon += ' tq-tree-node-control';
}
return treeIcon;
},

// private
doRender: function(cs, rs, ds, startRow, colCount, stripe){
// buffers
var rowBuffer = [];
var cellBuffer;
var cell;
var cellTemplate;
var cellProperties = {};
var rowProperties = {
tstyle: 'width:'+this.getTotalWidth()+';'
};
var record;
var depthBuffer = [];
var hasTreeCol;

for (var j = 0, len = rs.length; j < len; j++){
record = rs[j];
cellBuffer = [];
hasTreeCol = false;
var rowIndex = (j+startRow);

for (var i = 0; i < colCount; i++){
cell = cs[i];
cellProperties.id = cell.id;
cellProperties.css = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
cellProperties.attr = cellProperties.cellAttr = '';
cellProperties.value = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
cellProperties.style = cell.style;
if (Ext.isEmpty(cellProperties.value)){
cellProperties.value = ' ';
}
if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
cellProperties.css += ' x-grid3-dirty-cell';
}

if (cell.scope.treeCol && !hasTreeCol && record.node) {
hasTreeCol = true;
var node = record.node;

cellProperties.nodeIndent = this.getChildIndentUI(node);
cellProperties.nodeIconCls = node.attributes.iconCls || '';

cellProperties.nodeTreeIconCls = this.getTreeNodeIcon(node);

cellTemplate = this.templates.treeCell;
} else {
cellTemplate = this.templates.cell;
}
cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

}
var alt = [];

if (record.depth && record.node) {
// the check for startRow == 0 prevents hierarchy divs to be inserted
// when we only do a partial rendering - happens when data is changed
if (startRow == 0 && depthBuffer.length < record.depth) {
var cls = "x-tree-node-ct";
if (record.node.parentNode.isRoot || record.node.parentNode.expanded) {
cls += " tq-tree-node-ct-expanded";
} else {
cls += " tq-tree-node-ct-collapsed";
}
rowBuffer.push('<div class="'+cls+'">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}

if (this.staticTree) {
alt.push('tq-treegrid-static');
}

if (node.isLeaf()) {
alt.push('x-tree-node-leaf');
} else if (node.expanded){
alt.push('x-tree-node-expanded');
} else {
alt.push('x-tree-node-collapsed');
}

rowProperties.nodeId = record.node.id;
}
rowProperties.cols = colCount;
rowProperties.cells = cellBuffer.join('');

if(stripe && ((rowIndex+1) % 2 === 0)){
alt.push('x-grid3-row-alt');
}
if(record.dirty){
alt.push(' x-grid3-dirty-row');
}

if (this.getRowClass){
alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
}
rowProperties.alt = alt.join(' ');

rowBuffer[rowBuffer.length] = this.templates.row.apply(rowProperties);
}

while (depthBuffer.length) {
rowBuffer.push(depthBuffer.pop());
}
return rowBuffer.join('');
},

afterRender: function() {
Ext.ux.tree.GridView.superclass.afterRender.call(this);

if (!this.staticTree) {
this.mainBody.on('click', function(ev, el) {
this.toggleNodeEl(el);
}, this, {
delegate: '.tq-tree-node-control'
});
this.mainBody.on('dblclick', function(ev, el) {
this.toggleNodeEl(el);
}, this, {
delegate: '.x-tree-node-el'
});
}

this.grid.relayEvents(this, [ 'beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode' ]);
},

expandAll: function(maxDepth) {
maxDepth = maxDepth || Number.MAX_VALUE;
this.grid.getStore().each(function(r) {
if (!r.node.expanded && r.depth < maxDepth) {
this.toggleNodeRecord(r);
}
}, this);
},

collapseAll: function(maxDepth) {
maxDepth = maxDepth || 0;
this.grid.getStore().each(function(r) {
if (r.node.expanded && r.depth > maxDepth) {
this.toggleNodeRecord(r);
}
}, this);
},

toggleNodeRecord: function(record) {
var rowIndex = this.grid.getStore().indexOf(record);
var row = Ext.get(this.getRow(rowIndex));
this.toggleNodeRow(row);
},

toggleNodeRow: function(row) {
var record = this.grid.getStore().getAt(row.dom.rowIndex);
var node = record.node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded && this.fireEvent('beforecollapsenode', node, record, this.grid, this) !== false) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideOut('t', {
callback : function(){
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.hide();
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
}

} else if (this.fireEvent('beforeexpandnode', node, record, this.grid, this) !== false) {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideIn('t', {
callback : function(){
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.show();
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
}
}
}
},

toggleNodeEl: function(el) {
var row = Ext.get(this.findRow(el));
this.toggleNodeRow(row);
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}

});

Using the new source you are able to toggle idividual nodes with the toggleNodeEl(el), the toggleNodeRow(row) and the toggleNodeRecord(record) methods. toggleNodeEl(el) takes some HTMLElement and will toggle the row containing the element, toggleNodeRow(row) takes the HTMLElement of the grid row - this is probably the least useable of the three as it's not that easy to get hold of the row's HTMLElement (but it does all the hard work for the other two methods) and toggleNodeRecord(record) toggles the node based on the appropriate record.

If you have a reference to the record you want to check the expansion status on, you could easily check


record.node.expanded

mm_202
3 Aug 2010, 11:48 AM
teqneers, thanks again for the code! I actually wrote some of those functions myself, though yours are much cleaner :)

I do have one question for you, is it possible to get a row (object or rowIndex) from a Node directly??
I wrote a function to expand all nodes leading to a specific record, but I ended up having to use toggleNodeRecord() (also which took me a bit of time and console logging to figure out that node.id == rec.id :D)


expandToRow: function(row, maxDepth) { // Make sure all nodes leading to 'row' are expanded.
if(!Ext.isNumber(row)) row = row.rowIndex;
var rec = this.grid.getStore().getAt(row);
row=Ext.get(this.getRow(row));
maxDepth = maxDepth || Number.MAX_VALUE;
var node = rec.node;
//console.log('expandToRow:\t rec.id=[%s], rec.depth=%d\t row.id=[%s].\t node.id=[%s]',rec.id, rec.depth, row.id, node.id);

for(i=0;i<maxDepth;i++) {
//console.log('\t%d:\tnode=[%s],\tleaf=%s, expanded=%s', i, node.id, node.leaf ? node.leaf : 'NULL', node.expanded);

if(node.expanded==false)
{
var rec = this.grid.getStore().getById(node.id);
if(!rec) return true; // We've hit the top, we're done.
ret=this.toggleNodeRecord(rec);

//var row = this.findRow(node);
//console.log('\t\tCalling toggleNodeRow(row=[%s]), returned=[%s]', row.id, ret);
//console.log('\t\trec.id = [%d], recID=%s', rec.get('id'), rec.id);

}
node = node.parentNode;
if(!node) return true;
}
return false;
},
I left my debugging comments in so you could follow my train of thought in case I went at it totally wrong. The code that I was trying to get to work with getting the row directly from the node is there, too.
Any help or pointers would be much appreciated :)



For others using this, I'll post my modified functions / additions to teqneers's Ext.ux.tree.GridView(), it is basically the same but just a few differences:


Any 'row' function can take a row object or index.
focusRow(ri) Makes sure that that row is visible and focused. (like the original Ext.grid.GridView)
ExpandToRow(row) Makes sure that all nodes that lead to that row are expanded. Returns true if successful.
isRowExpanded(row) Returns true/false or null if a leaf.
toggleNodeRow(row) / toggleNodeRecord(row) return 'collapsed'/'expanded' or null depending on what it did.




isRowExpanded: function(row) { // Returns true/false or null if row is leaf.
if(!Ext.isNumber(row)) row = row.rowIndex;
var rec = this.grid.getStore().getAt(row);
if(rec.node.isLeaf()) return null;
return rec.node.expanded;
},

focusRow: function(ri) {
this.expandToRow(ri);
this.focusCell(ri, 0, false);
},

expandToRow: function(row, maxDepth) { // Make sure all nodes leading to 'row' are expanded.
if(!Ext.isNumber(row)) row = row.rowIndex;
var rec = this.grid.getStore().getAt(row);
row=Ext.get(this.getRow(row));
maxDepth = maxDepth || Number.MAX_VALUE;
var node = rec.node;

for(i=0;i<maxDepth;i++) {
if(node.expanded==false)
{
var rec = this.grid.getStore().getById(node.id);
if(!rec) return true; // We've hit the top, we're done.
ret=this.toggleNodeRecord(rec);
}
node = node.parentNode;
if(!node) return true;
}
return false;
},

toggleNodeRecord: function(record) {
var rowIndex = this.grid.getStore().indexOf(record);
var row = Ext.get(this.getRow(rowIndex));
return this.toggleNodeRow(row);
},

toggleNodeRow: function(row) {
if(typeof(row)=='number') row=this.getRow(row);
if(!row) return null; row=Ext.get(row);
var record = this.grid.getStore().getAt(row.dom.rowIndex);
var node = record.node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded && this.fireEvent('beforecollapsenode', node, record, this.grid, this) !== false) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideOut('t', {
callback : function(){
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.hide();
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
}
return 'collapsed';

} else if (this.fireEvent('beforeexpandnode', node, record, this.grid, this) !== false) {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideIn('t', {
callback : function(){
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.show();
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
}
return 'expanded';
}
}
return false;
},

toggleNodeEl: function(el) {
var row = Ext.get(this.findRow(el));
return this.toggleNodeRow(row);
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}
});

sudhirhv
18 Aug 2010, 4:30 AM
Hi teqneers,

Thanks!

This is a beautiful contribution! I can't tell you how timely your thread has been for me.

- Sudhir

Windekewaai
14 Sep 2010, 7:41 AM
Can we get a full working example of this TreeGrid?

My data is put in the grid in a wrong format. The result from the server is correct (children, leafs, ...), yet the grid shows all records in level 1 (so no tree, just a listing of every record).

ivanleblanc
11 Oct 2010, 6:25 AM
Has anyone attempted to make this TreeGrid async?

I attempted to try and do this, but seems to be more difficult than it appears because this component is not dealing with a TreePanel component but a Grid that looks like one. So all the functionality of AsyncTreeNode will not work properly.

teqneers
11 Oct 2010, 6:33 AM
That's correct. This approach uses a regular grid and not the tree panel. And that was exactly the trade-off we had to make when building this component: implement all the editing capabilities of the Ext.grid.EditorGridPanel into a custom tree panel or implement the needed tree-interface into custom grid components (data reader and grid view) and reuse things like the grid column model and the editing capabilities. As we were in no need for any asynchronous behavior the decision was quite easy.

And of course you're right, implementing asynchronous features is very difficult and would require a lot of work because the whole data store mechanism cannot deal with hierarchical data at all.

Windekewaai
11 Oct 2010, 6:47 AM
Indeed, sadly enough I needed a TreeGrid with editor functions for an async store.

I found the solution in maximgb's TreeGrid component.
http://www.max-bazhenov.com/dev/ux.maximgb.tg/index.php
It takes a bit of trial and error, but eventually you'll get a shiny TreeGrid that does just about everything you want :)

jay@moduscreate.com
11 Oct 2010, 7:53 AM
moved to extensions and plugins

pratik016
22 Nov 2010, 9:40 PM
First thank you to ' teqneers (http://www.sencha.com/forum/member.php?56052-teqneers)' to provide this way for working with tree... and finally thankfull to mm_202... your work is also really great. I was just going to quit-up and found your solution. thanks again ~o)

pratik016
23 Nov 2010, 2:06 AM
I have problem while edit the grid in treegrideditor.>:)
I have added one column for radiobutton. for that I extend it to class.
-----------
var radioColumn = new Ext.grid.RadioRow({
header: 'Select',
dataIndex: 1,
name: 'fun_rad_id',
id: 'fun_rad_id',
width: 75


});
------------
plugins: radioColumn, at the grid properties and added 1 colum named radioColumn.

But what happen here... after edit the row. tree css has been gone and look like a simple grid /:):-/

Please help me on this.

screen shots attached :
2347123472

feiq
26 Jan 2011, 11:39 PM
Hello teqneers and everybody:

First, thanks for your great code, it help me solve my problem mostly. But I find a new problem, that is when I render the tree grid, the node(not leaf) is always collapsed but it's children is rendered, even though I don't set the node's 'expanded' property to true.

How should I solve the problem? Thanks.

amanind
25 Mar 2011, 4:48 AM
Thanks for developing excellent treegrid

can anyone help me and tell me how to show scroll horizontally and vertically?
How to show all the nodes collapsed at load time?

amanind
1 Apr 2011, 4:35 AM
Can anyone help me on my queries in the above thread

teqneers
1 Apr 2011, 4:41 AM
Hi amanind,

If your nodes contain an "expanded"-property that's set to "false" all the tree nodes will be collapsed initially.
Scrolling could be somewhat complicated because a lot of factors determine if scrollbars are shown (Browser, some properties being set, layout issues or CSS settings). It's best if you can show the relevant code - most suitable would be a stripped down version of your code that shows the current scrolling issue.

Best regards
Stefan

amanind
1 Apr 2011, 4:49 AM
Stefan, thanks for your reply.
After setting "expanded" property to false in json, it is still showing expanded node on page

teqneers
1 Apr 2011, 4:58 AM
You should show some code...

amanind
1 Apr 2011, 5:02 AM
Here is my JS code



Ext.ux.tree.TreeReader = Ext.extend(Ext.data.DataReader, {

tree: null,
jsonData: null,

constructor: function(meta, recordType) {
Ext.ux.tree.TreeReader.superclass.constructor.call(this, meta, recordType || meta.fields);
},

load: function(node){
if (node.attributes.children){
var cs = node.attributes.children;
for (var i = 0, len = cs.length; i < len; i++){
if (cs[i]) {
var cn = node.appendChild(this.createNode(cs[i]));
this.load(cn);
}
}
}
},

createNode: function(attr){
var node = new Ext.data.Node(attr);
node.expanded = (attr.expanded === true);
return node;
},

read: function(response) {
var json = response.responseText;
var o = Ext.decode(json);
if (!o) {
throw { message: 'JsonReader.read: Json object not found' };
}
return this.readRecords(o);
},

/**
* Create a data block containing Ext.data.Records from a tree.
*/
readRecords: function(o) {
this.jsonData = o;

var items = o;
if (this.meta.root && o[this.meta.root] ) {
items = o[this.meta.root];
if (o.metaData) {
this.onMetaChange(o.metaData);
}
}

var root = this.createNode({
text: 'Root',
id: 'root',
children: items
});
this.tree = new Ext.data.Tree(root);
this.load(root);

var f = this.recordType.prototype.fields;
var records = [];
root.cascade(function(node) {
if (node !== root) {
var record = new this.recordType(this.extractValues(node, f.items), node.id);
record.node = node;
record.depth = node.getDepth();
records.push(record);
}
}, this);

return {
success : true,
records : records,
totalRecords : records.length
};
},


onMetaChange : function(meta){
Ext.apply(this.meta, meta);
},

/**
* type-casts a single node
*/
extractValues : function(node, fields) {
var f, m, values = {};
for(var j = 0; j < fields.length; j++){
f = fields[j];
m = f.mapping || f.name;
var v = node.attributes[m];
values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue, node);
}
return values;
}
});

Ext.ux.tree.GridView = Ext.extend(Ext.grid.GridView, {

useArrows: true,
staticTree: false,
animate: false,
constructor: function(config) {
this.emptyIcon = Ext.BLANK_IMAGE_URL;

Ext.ux.tree.GridView.superclass.constructor.call(this, config);
this.templates = {};
this.templates.master = new Ext.Template(
'<div class="x-grid3 tq-treegrid" hidefocus="true" style="width:1200px;display:block;overflow:scroll !important;height:100%;">',
'<div class="x-grid3-viewport">',
'<div class="x-grid3-header">',
'<div class="x-grid3-header-inner">',
'<div class="x-grid3-header-offset" style="{ostyle}">{header}</div>',
'</div>',
'<div class="x-clear"></div>',
'</div>',
'<div class="x-grid3-scroller">',
'<div class="x-grid3-body ', (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') , '" style="{bstyle}">{body}</div>',
'<a href="#" class="x-grid3-focus" tabIndex="-1"></a>',
'</div>',
'</div>',
'<div class="x-grid3-resize-marker"> </div>',
'<div class="x-grid3-resize-proxy"> </div>',
'</div>'
);

this.templates.row = new Ext.Template(
'<div class="x-grid3-row {alt}" style="{tstyle}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody class="x-tree-node">',
'<tr>{cells}</tr>',
(this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
'</tbody>',
'</table>',
'</div>'
);
this.templates.treeCell = new Ext.Template(
'<td class="tq-treegrid-col x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div ext:tree-node-id="{nodeId}" class="x-grid3-col-{id} x-tree-node-el x-unselectable" unselectable="on" {attr}>',
'<div class="tq-treegrid-icons">',
'<span class="x-tree-node-indent">{nodeIndent}</span>',
'<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow {nodeTreeIconCls}" />',
'<img src="', this.emptyIcon, '" class="x-tree-node-icon {nodeIconCls}" unselectable="on" />',
'</div>',
'<div class="x-grid3-cell-inner" unselectable="on" {attr}>',
'{value}',
'</div>',
'</div>',
'</td>'
);

this.addEvents('beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode');
},

getChildIndentUI: function(node) {
var indentBuffer = [];
var parentNode = node.parentNode;
while (parentNode){
if (!parentNode.isRoot){
if (!parentNode.isLast()) {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
} else {
indentBuffer.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
}
}
parentNode = parentNode.parentNode;
}
return indentBuffer.join("");
},

getTreeNodeIcon: function(node) {
var treeIcon = node.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
if (node.hasChildNodes() && !this.staticTree){
if (node.expanded){
treeIcon += "-minus";
} else {
treeIcon += "-plus";
}
treeIcon += ' tq-tree-node-control';
}
return treeIcon;
},

// private
doRender: function(cs, rs, ds, startRow, colCount, stripe){
// buffers
var rowBuffer = [];
var cellBuffer;
var cell;
var cellTemplate;
var cellProperties = {};
//debugger
var rowProperties = {
tstyle: 'width:'+this.getTotalWidth()+';'
};
var record;
var depthBuffer = [];
var hasTreeCol;

for (var j = 0, len = rs.length; j < len; j++){
record = rs[j];
cellBuffer = [];
hasTreeCol = false;
var rowIndex = (j+startRow);

for (var i = 0; i < colCount; i++){
cell = cs[i];
cellProperties.id = cell.id;
cellProperties.css = (i === 0) ? 'x-grid3-cell-first ' : (i == (colCount - 1) ? 'x-grid3-cell-last ' : '');
cellProperties.attr = cellProperties.cellAttr = '';
cellProperties.value = cell.renderer.call(cell.scope, record.data[cell.name], cellProperties, record, rowIndex, i, ds);
cellProperties.style = cell.style;
if (Ext.isEmpty(cellProperties.value)){
cellProperties.value = ' ';
}
if (this.markDirty && record.dirty && Ext.isDefined(record.modified[cell.name])){
cellProperties.css += ' x-grid3-dirty-cell';
}

if (cell.scope.treeCol && !hasTreeCol && record.node) {
hasTreeCol = true;
var node = record.node;

cellProperties.nodeIndent = this.getChildIndentUI(node);
cellProperties.nodeIconCls = node.attributes.iconCls || '';

cellProperties.nodeTreeIconCls = this.getTreeNodeIcon(node);

cellTemplate = this.templates.treeCell;
} else {
cellTemplate = this.templates.cell;
}
cellBuffer[cellBuffer.length] = cellTemplate.apply(cellProperties);

}
var alt = [];

if (record.depth && record.node) {
// the check for startRow == 0 prevents hierarchy divs to be inserted
// when we only do a partial rendering - happens when data is changed
if (startRow == 0 && depthBuffer.length < record.depth) {
var cls = "x-tree-node-ct";
if (record.node.parentNode.isRoot || record.node.parentNode.expanded) {
cls += " tq-tree-node-ct-expanded";
} else {
cls += " tq-tree-node-ct-collapsed";
}
rowBuffer.push('<div class="'+cls+'">');
depthBuffer.push('</div>');
} else {
while (depthBuffer.length > record.depth) {
rowBuffer.push(depthBuffer.pop());
}
}

if (this.staticTree) {
alt.push('tq-treegrid-static');
}

if (node.isLeaf()) {
alt.push('x-tree-node-leaf');
} else if (node.expanded){
alt.push('x-tree-node-expanded');
} else {
alt.push('x-tree-node-collapsed');
}

rowProperties.nodeId = record.node.id;
}
rowProperties.cols = colCount;
rowProperties.cells = cellBuffer.join('');

if(stripe && ((rowIndex+1) % 2 === 0)){
alt.push('x-grid3-row-alt');
}
if(record.dirty){
alt.push(' x-grid3-dirty-row');
}

if (this.getRowClass){
alt.push(this.getRowClass(record, rowIndex, rowProperties, ds));
}
rowProperties.alt = alt.join(' ');

rowBuffer[rowBuffer.length] = this.templates.row.apply(rowProperties);
}

while (depthBuffer.length) {
rowBuffer.push(depthBuffer.pop());
}
return rowBuffer.join('');
},

afterRender: function() {
Ext.ux.tree.GridView.superclass.afterRender.call(this);

if (!this.staticTree) {
this.mainBody.on('click', function(ev, el) {
this.toggleNodeEl(el);
}, this, {
delegate: '.tq-tree-node-control'
});
this.mainBody.on('dblclick', function(ev, el) {
this.toggleNodeEl(el);
}, this, {
delegate: '.x-tree-node-el'
});
}

this.grid.relayEvents(this, [ 'beforecollapsenode', 'beforeexpandnode', 'collapsenode', 'expandnode' ]);
},

expandAll: function(maxDepth) {
maxDepth = maxDepth || Number.MAX_VALUE;
this.grid.getStore().each(function(r) {
if (!r.node.expanded && r.depth < maxDepth) {
this.toggleNodeRecord(r);
}
}, this);
},

collapseAll: function(maxDepth) {
maxDepth = maxDepth || 0;
this.grid.getStore().each(function(r) {
if (r.node.expanded && r.depth > maxDepth) {
this.toggleNodeRecord(r);
}
}, this);
},

toggleNodeRecord: function(record) {
var rowIndex = this.grid.getStore().indexOf(record);
var row = Ext.get(this.getRow(rowIndex));
this.toggleNodeRow(row);
},

toggleNodeRow: function(row) {
var record = this.grid.getStore().getAt(row.dom.rowIndex);
var node = record.node;

var childCnt = row.next('.x-tree-node-ct', false);
var nodeCtrl = row.child('.tq-tree-node-control', false);

if (childCnt && nodeCtrl) {
if (node.expanded && this.fireEvent('beforecollapsenode', node, record, this.grid, this) !== false) {
childCnt.enableDisplayMode('block');
childCnt.stopFx();

row.removeClass('x-tree-node-expanded');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.addClass('x-tree-node-collapsed');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideOut('t', {
callback : function(){
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.hide();
node.expanded = false;
childCnt.removeClass('tq-tree-node-ct-expanded');
childCnt.addClass('tq-tree-node-ct-collapsed');
this.fireEvent('collapsenode', node, record, this.grid, this);
}

} else if (this.fireEvent('beforeexpandnode', node, record, this.grid, this) !== false) {
childCnt.stopFx();

row.addClass('x-tree-node-expanded');
nodeCtrl.addClass(node.isLast() ? "x-tree-elbow-end-minus" : "x-tree-elbow-minus");
row.removeClass('x-tree-node-collapsed');
nodeCtrl.removeClass(node.isLast() ? "x-tree-elbow-end-plus" : "x-tree-elbow-plus");

if (this.animate) {
childCnt.slideIn('t', {
callback : function(){
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
},
scope: this,
duration: .25
});
} else {
childCnt.show();
node.expanded = true;
childCnt.removeClass('tq-tree-node-ct-collapsed');
childCnt.addClass('tq-tree-node-ct-expanded');
this.fireEvent('expandnode', node, record, this.grid, this);
}
}
}
},

toggleNodeEl: function(el) {
var row = Ext.get(this.findRow(el));
this.toggleNodeRow(row);
},

getRows: function() {
return this.hasRows() ? this.mainBody.query(this.rowSelector) : [];
}

});


MS.ImpactSisterApp.FinancialProjectionsPanel = Ext.extend(Ext.grid.GridPanel, {

initComponent: function() {
var config = {
autoHeight: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: {
xtype: 'store',
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [
{ name: 'id', mapping: 'id', type: 'int' },
{ name: 'name', mapping: 'name' },
{ name: 'Sync', mapping: 'Sync', type: 'date' },
{ name: 'validate', mapping: 'validate' },
{ name: 'version', mapping: 'version' },
{ name: 'model_status', mapping: 'model_status' },
{ name: 'ev_status', mapping: 'ev_status' },
{ name: 'val_comm', mapping: 'val_comm' },
{ name: 'updated_by', mapping: 'updated_by' },
{ name: 'updated_on', mapping: 'updated_on', type: 'date' }]
}),
data: [{
id : 1,
name : 'MSREF I',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 11,
name : 'America',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 111,
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 1111,
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1112,
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1113,
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1114,
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1115,
name : 'Model5',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1116,
name : 'Model6',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1117,
name : 'Model7',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1118,
name : 'Model8',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
}]
},
{
id : 112,
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 1121,
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1122,
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1123,
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1124,
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1125,
name : 'Model5',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1126,
name : 'Model6',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1127,
name : 'Model7',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
},
{
id : 1128,
name : 'Model8',
Sync : '\/Date(1254283200000-0400)\/',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf : true
}]
}]
},
{
id : 12,
name : 'Asia',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 121,
name : 'Thing ABA',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
},
{
id : 122,
name : 'Thing ABB',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
}]
}]
},
{
id : 2,
name : 'MSREF II',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 21,
name : 'America',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 211,
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
},
{
id : 212,
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
}]
},
{
id : 22,
name : 'Europe',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : false,
expanded : false,
children : [{
id : 221,
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
},
{
id : 222,
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : 'samandee',
updated_on : '',
leaf : true
}]
}]
}]
},
columns: [
{ header: 'Id', dataIndex: 'id', hidden:true },
{ header: 'Fund/Region/Investment/Model', id: 'col-name', dataIndex: 'name', treeCol: true, width: 250 },
{ header: 'Sync Status', dataIndex: 'Sync', width: 100 },
{ header: 'Validation', dataIndex: 'validate', width: 100 },
{ header: 'Version', dataIndex: 'version', width: 100 },
{ header: 'Model Status', dataIndex: 'model_status', width: 100 },
{ header: 'EV Carry Value Status', dataIndex: 'ev_status', width: 100 },
{ header: 'Val Comm Control', dataIndex: 'val_comm', width: 100 },
{ header: 'Last Updated By', dataIndex: 'updated_by', width: 100 },
{ header: 'Last Updated On', dataIndex: 'updated_on', width: 100 }
],
style: 'font-size:8pt;font-family:helvetica;overflow:auto;',
autoScroll: true,
style: 'width:1200px',
width: 1200,
height: 300,
frame: true,

plugins: [
new Ext.ux.grid.ColumnHeaderGroup({
rows: [
[{
colspan:5
},
{
header: 'Asset Management Review',
align: 'center'
},
{
header: 'Investment Control Review',
align: 'center'
},
{
header: 'GPG',
align: 'center'
},{
colspan:2
}]
]
})
],
closable: true
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
MS.ImpactSisterApp.FinancialProjectionsPanel.superclass.initComponent.apply(this, arguments);

//this.on('cellclick', this.modelSelected, this);
//this.getSelectionModel().on('beforerowselect', this.beforeRowSelect, this);
//this.gridFilter.on("select", this.loadSelectedFilterType, this);
//this.on('render', this.onGridRender, this);
//this.on('columnresize', this.columnResize, this);
//this.memberTypeCombo.on("select", this.setSelectedMemberType, this);
//this.store.loadData(this.dataRows[1]);
//this.refreshInvestmentAssetModelsPanel(this.dataRows);

MS.ImpactSisterApp.SisterAppEventManager.on("setReportingPeriod", this.setReportingPeriod, this);
},

setReportingPeriod: function() {
this.reportingPeriodIdHold = arguments[0].Id;
this.reportingPeriodNameHold = arguments[0].Name;
},

refreshInvestmentAssetModelsPanel: function() {
debugger
if(arguments[0] && arguments[0].length > 0)
{
this.dataRows = arguments[0][1];
}else{
this.dataRows = [];
}
try{
this.store.removeAll();
}catch(ex){
}
this.store.loadData(this.dataRows);
},

loadSelectedFilterType: function(cmb, rec , idx) {
MS.ImpactSisterApp.ShowLoadingMask(true);
this.showAllQuarters = idx==0?false:true;
var params = {
method: "getInvestmentAndChildrenAssetModels",
showAllQuarters: this.showAllQuarters,
investmentId: this.investmentId,
investmentName: this.investmentName,
reportingPeriodId: idx==0?this.reportingPeriodIdHold : 0,
reportingPeriodName: idx==0? this.reportingPeriodNameHold : 0,
reportingPeriodIdHold: this.reportingPeriodIdHold,
reportingPeriodNameHold: this.reportingPeriodNameHold,
permission: this.permission
}
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('getInvestmentAndChildrenAssetModels', params);
if(this.reportingPeriodId == 0){
this.gridFilter.setValue("All Quarters");
this.showAllQuarters = true;
}else{
this.gridFilter.setValue("Selected Quarter");
this.showAllQuarters = false;
}
},

setSelectedMemberType: function() {
var seltype = this.memberType[this.memberTypeCombo.selectedIndex][2];
if (this.selectedMemberType != seltype){
for (var i = 0; i < this.dataRows.length; i++) {
if(this.dataRows[i]._cashData != null){
this.dataRows[i].irr = this.dataRows[i]._cashData[seltype].IRR;
this.dataRows[i].multiple = this.dataRows[i]._cashData[seltype].Multiple;
this.dataRows[i].total_contribution = this.dataRows[i]._cashData[seltype].TotalContribution;
this.dataRows[i].total_distribution = this.dataRows[i]._cashData[seltype].TotalDistribution;
this.dataRows[i].total_profit = this.dataRows[i]._cashData[seltype].TotalProfit;
}
}
this.store.removeAll();
this.store.loadData(this.dataRows);
this.selectedMemberType = seltype;
}
},

onGridRender: function(grid) {
//this.getView().breadcrumbs_el.parent().remove();
//if(this.permission && this.permission.IsEntitled)Ext.getCmp(this.id + "-btnNewModel").setVisible(true); //Change here
},

modelSelected: function(grid, rowIndex, cellIndex, e) {
var record = grid.getStore().getAt(rowIndex);
var fieldName = grid.getColumnModel().getDataIndex(cellIndex);
var data = record.get(fieldName);
var target = e.getTarget();
var id = Ext.id(target);
var view = this.getView();
if (fieldName == '_icons') {
if (id.indexOf('export_to_excel') >= 0) {
this.exportModelToExcel(record.get('id').split('.')[0]);
}
if (id.indexOf('copy_inv') >= 0) {
this.copyModel(record, record.get('id').split('.')[0]);
}
if (id.indexOf('delete_inv') >= 0) {
if(record.data._isCopyForward){
Ext.Msg.alert('Delete Model', "Cannot delete "+MS.ImpactSisterApp.constants.versionTypes["BL"]+" or "+MS.ImpactSisterApp.constants.versionTypes["UW"]+" versions. <br />Please change version type to "+MS.ImpactSisterApp.constants.versionTypes["OT"]+" and then delete.");
return;
}else{
Ext.MessageBox.confirm('Delete Model', 'Do you really want to delete Model "' + record.data.model_name + '"?',
function(btn) {
if (btn == 'yes') {
MS.ImpactSisterApp.ShowLoadingMask(true);
var ModelId = parseInt(record.data.id);
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('setModelName', { Id: ModelId, Name: record.data.model_name });
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('deleteModel', { method: "deleteModel", investmentId: this.investmentId, investmentName: this.investmentName, reportingPeriodId: this.reportingPeriodId, reportingPeriodName: this.reportingPeriodName, ModelId: ModelId, showAllQuarters: this.showAllQuarters });
} else {
}
}, this);
}
}
}
if (record.data.description && record.data.description.indexOf("Add Model") !== -1 && fieldName == 'description') {
Ext.MessageBox.confirm('Add Model', 'Do you really want to add Model for Asset "' + record.data.model_name + '"?',
function(btn) {
if (btn == 'yes') {
MS.ImpactSisterApp.ShowLoadingMask(true);
var AssetId = parseInt(record.data.id);
var InvModelId = parseInt(record.data._parent);
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('saveNewAssetModel', { method: "saveNewAssetModel", investmentId: this.investmentId, investmentName: this.investmentName, reportingPeriodId: this.reportingPeriodId, reportingPeriodName: this.reportingPeriodName, AssetId: AssetId, InvModelId: InvModelId });
} else {

}
}, this);
} else {
if (fieldName == 'model_status' && record.data.permission.CanChangeStatus && !record.data._is_leaf) {
if (target.childNodes.length == 0) {
var sCombo = new MS.ImpactSisterApp.SimpleStoreCombo({ style: 'font-size:11px;padding:1px 5px', width: 90, applyTo: e.target })
MS.ImpactSisterApp.setComboSimpleStore(sCombo, MS.ImpactSisterApp.config.comboFieldTypes.statusType, MS.ImpactSisterApp.config.comboDataTypes.statusType);
sCombo.on("select", function(inputField, selectedRecord) {

var inputFieldHold = target.defaultValue;
var gridScope = this;
Ext.Msg.confirm('Model Status', 'Are you sure you want to change model status?', function(btn, text) {
if (btn == 'yes') {
this._changeStatus = target.value;
if(record.hasChildren){
for(var i=0; i<gridScope.store.data.items.length; i++){
if(record.id == gridScope.store.data.items[i].data._parent){
gridScope.store.data.items[i].set('model_status', target.value);
}
}
}
/*MS.ImpactSisterApp.ShowLoadingMask(true);
record.data.model_type = selectedRecord.data.Name;
var versionParams = Ext.encode(record.data);
var modelId = parseInt(record.data.id);
var ModelStatusId = selectedRecord.data.Id;
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('saveModelStatus', { method: "saveModelStatus", showAllQuarters: gridScope.showAllQuarters, investmentId: gridScope.investmentId, investmentName: gridScope.investmentName, reportingPeriodId: gridScope.reportingPeriodId, reportingPeriodName: gridScope.reportingPeriodName, ModelId: modelId, ModelStatusId: ModelStatusId });*/
} else {
inputField.value = inputFieldHold;
inputField.defaultValue = inputFieldHold;
sCombo.setValue(inputFieldHold);
}
/*inputField.value = inputFieldHold;
inputField.defaultValue = inputFieldHold;
sCombo.setValue(inputFieldHold);*/
});
}, this)
sCombo.render();
}
}
if (fieldName == 'model_type' && record.data.permission.CanChangeVersion && !record.data._is_leaf) {
if (target.childNodes.length == 0) {
var vCombo = new MS.ImpactSisterApp.SimpleStoreCombo({ style: 'font-size:11px;padding:1px 5px', width: 90, resizable: true, applyTo: e.target })
MS.ImpactSisterApp.setComboSimpleStore(vCombo, MS.ImpactSisterApp.config.comboFieldTypes.versionType, this.modelVersions/*MS.ImpactSisterApp.config.comboDataTypes.versionType*/);
vCombo.on("select", function(inputField, selectedRecord) {
var inputFieldHold = target.defaultValue;
var gridScope = this;
Ext.Msg.confirm('Model Version', 'Are you sure you want to change the version type', function(btn, text) {
if (btn == 'yes') {
MS.ImpactSisterApp.ShowLoadingMask(true);
record.data.model_type = selectedRecord.data.Name;
var versionParams = Ext.encode(record.data);
var modelId = parseInt(record.data.id);
var ModelTypeId = selectedRecord.data.Id;
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('saveModelVersion', { method: "saveModelVersion", showAllQuarters: gridScope.showAllQuarters, investmentId: gridScope.investmentId, investmentName: gridScope.investmentName, reportingPeriodId: gridScope.reportingPeriodId, reportingPeriodName: gridScope.reportingPeriodName, ModelId: modelId, ModelTypeId: ModelTypeId });
}
inputField.value = inputFieldHold;
inputField.defaultValue = inputFieldHold;
vCombo.setValue(inputFieldHold);
});
}, this)
vCombo.render();
}
}
// to load grid in south panel onclick
if (fieldName == 'model_name') {
if (!record.data._noasset && target.tagName == "A") {
var panel = this.appManager.getViewportManager().getSouthPanel()
// var panel = MS.ImpactSisterApp.SisterApplication.getAppManager().getViewportManager().getSouthPanel();
var tabs = panel.items;
if(tabs.length > 2){
var tabFound = false;
for(var i = 0; i < tabs.items.length; i++){
if(record.data.model_name == tabs.items[i].modelName && parseInt(record.data._id) == tabs.items[i].id){
panel.setActiveTab(tabs.items[i]);
tabFound = true;
}
}
if(!tabFound){
Ext.MessageBox.alert('Too many open models', '<br />No more than 3'
+ ' models can be open at once.<br /><br /> Please close some model tabs'
+ ' before opening a new one.');
}
}else{
MS.ImpactSisterApp.ShowPanelLoadingMask(true,"south_panel");
MS.ImpactSisterApp.SisterAppEventManager.fireEvent('setModelName', { Id: record.get('id').split('.')[0], Name: record.get('model_name').split('$')[0]});
MS.ImpactSisterApp.SisterAppEventManager.fireEvent("checkConnection", {"method":"checkConnection","event":"showInvestmentModel", investmentId: this.investmentId, modelId: record.get('id').split('.')[0] });
}
}
}
}
},

beforeRowSelect: function(sm, rowIndex, keepExisting, rec) {
return false;
},

columnResize: function(col, width) {
var minWidth = this.getColumnModel().config[col].minWidth;
if (width < minWidth) {
this.getColumnModel().setColumnWidth(col, minWidth);
return false;
}
},

doResize: function(grid, newWidth, newHeight, oldWidth, oldHeight) {
var cm = grid.colModel;
var i = cm.getIndexById('desc');
var col = cm.getColumnById('desc');
if (this.width > 0) {
cm.setColumnWidth(i, col.width + newWidth - this.width);
this.width = this.getSize().width;
}
},

onDestroy: function() {
this.un('cellclick', this.modelSelected, this);
this.getSelectionModel().un('beforerowselect', this.beforeRowSelect, this);
this.gridFilter.un("select", this.loadSelectedFilterType, this);
this.un('render', this.onGridRender, this);
this.un('columnresize', this.columnResize, this);
this.memberTypeCombo.un("select", this.setSelectedMemberType, this);
MS.ImpactSisterApp.SisterAppEventManager.un("setReportingPeriod", this.setReportingPeriod, this);
}
});


MS.ImpactSisterApp.FinancialProjectionsPanel.prototype.formatNumber = function(value, meta, rec) {
if (!rec.data._showAsTitle) {
if(meta.id == "multiple" && value == 99999999){
var val = "Infinite";
}else{
var val = Ext.util.Format.formatNumber(value);
}
if(rec.data._isInQueue){
return '<span class="model-detail-isinqueue">' + val + '</span>';
}else{
return val;
}
}
};

MS.ImpactSisterApp.FinancialProjectionsPanel.prototype.showCombo = function(val, meta, rec, row, col, store) {
//return '<div style="font-size:11px"><input style="cursor:pointer;border:1px solid lightblue;font-size:11px;font-family:tahoma;width:80px;padding:3px 5px" type="text" value="' + rec.data.Status.Name + '" size="20"/></div>';
};


function trim(stringToTrim) {
return stringToTrim.replace(/^\s+|\s+$/g,"");
}
function ltrim(stringToTrim) {
return stringToTrim.replace(/^\s+/,"");
}
function rtrim(stringToTrim) {
return stringToTrim.replace(/\s+$/,"");
}

teqneers
1 Apr 2011, 5:19 AM
Which ExtJS version do you use?

And please: do not post all of your code... It's absolutely sufficient if you post the relevant code - remove as much code as possible to show the problem. That makes debugging much simpler. And it'd be great if the code you're going to post would run with a standard setup of ExtJS - without the need to include UX libraries or other application code.

teqneers
1 Apr 2011, 5:22 AM
Can it be the case that you're missing some CSS?


/**
* Tree grid
*/
.tq-treegrid .tq-treegrid-col {
border: none;
}

.tq-treegrid .tq-treegrid-icons {
float: left;
}

/**
* the 5000px-width-hack is required as IE6/7 will wrap text not fitting into the cell
* to the new line actually hiding the text altogether as the rows do not expand in height
*/
.ext-ie6 .tq-treegrid .x-tree-node-el,
.ext-ie7 .tq-treegrid .x-tree-node-el {
width: 5000px;
}

.tq-treegrid .x-tree-node-el {
line-height: 13px;
padding: 1px 3px 1px 5px;
}

.tq-treegrid .tq-tree-node-ct-collapsed {
display: none;
}

.tq-treegrid .tq-treegrid-static .x-tree-ec-icon {
display: none;
}

.tq-treegrid .tq-treegrid-static .x-tree-node-el {
cursor: default;
}

Especially the rule .tq-treegrid .tq-tree-node-ct-collapsed seems to be missing.

amanind
1 Apr 2011, 5:28 AM
You are right brother , I am missing CSS.
Now nodes are coming in collapsed mode in starting after applying your css, now the problem is only scroll is not coming.

Thanks alot

amanind
1 Apr 2011, 5:29 AM
And I am using version 3.1.0

teqneers
1 Apr 2011, 5:39 AM
Remove the autoHeight property....

And you're messing around with a lot of size properties and even inline-styles... You shouldn't do that because that will wreak havoc with all the internal calculations.

amanind
1 Apr 2011, 5:47 AM
One question more.
Right now I am using JSON code in the JS file. I want to call it from outside means I want to take out JSON data from JS file.

After creating JSON file how to call it in the JS file

teqneers
1 Apr 2011, 6:13 AM
You can use the standard components to load data asynchronously via XHR. Just configure your store with an appropriate proxy. There are examples available on sencha.com as well as in the documentation (http://dev.sencha.com/deploy/dev/docs/?class=Ext.data.DataProxy).

amanind
3 Apr 2011, 11:33 PM
Hi Stefan,

Can you tell me please what is wrong in my code, I am not able to load JSON



initComponent: function() {
debugger
var store = new Ext.data.Store({
xtype: 'store',
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [
{ name: 'id', mapping: 'id', type: 'int' },
{ name: 'name', mapping: 'name' },
{ name: 'Sync', mapping: 'Sync', type: 'date' },
{ name: 'validate', mapping: 'validate' },
{ name: 'version', mapping: 'version' },
{ name: 'model_status', mapping: 'model_status' },
{ name: 'ev_status', mapping: 'ev_status' },
{ name: 'val_comm', mapping: 'val_comm' },
{ name: 'updated_by', mapping: 'updated_by' },
{ name: 'updated_on', mapping: 'updated_on', type: 'date' }]
}),
url: MS.ImpactSisterApp.testUrl.getInvestments
});
var config = {
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: this.store,
columns: [
{ header: 'Id', dataIndex: 'id', hidden:true },
{ header: 'Fund/Region/Investment/Model', id: 'col-name', dataIndex: 'name', treeCol: true, width: 250 },
{ header: 'Sync Status', dataIndex: 'Sync', width: 100 },
{ header: 'Validation', dataIndex: 'validate', width: 100 },
{ header: 'Version', dataIndex: 'version', width: 100 },
{ header: 'Model Status', dataIndex: 'model_status', width: 100 },
{ header: 'EV Carry Value Status', dataIndex: 'ev_status', width: 100 },
{ header: 'Val Comm Control', dataIndex: 'val_comm', width: 100 },
{ header: 'Last Updated By', dataIndex: 'updated_by', width: 100 },
{ header: 'Last Updated On', dataIndex: 'updated_on', renderer: this.forwardCurves, width: 100 }
],
style: 'font-size:8pt;font-family:helvetica;overflow:auto;',
autoScroll: true,

plugins: [
new Ext.ux.grid.ColumnHeaderGroup({
rows: [
[{
colspan:5
},
{
header: 'Asset Management Review',
align: 'center'
},
{
header: 'Investment Control Review',
align: 'center'
},
{
header: 'GPG',
align: 'center'
},{
colspan:2
}]
]
})
],
closable: true
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
MS.ImpactSisterApp.FinancialProjectionsPanel.superclass.initComponent.apply(this, arguments);
}

teqneers
3 Apr 2011, 11:40 PM
What is MS.ImpactSisterApp.testUrl.getInvestments? What exactly is the problem? Check if a connection is made to the server (use Firebug or something similar).

amanind
4 Apr 2011, 12:01 AM
MS.ImpactSisterApp.testUrl.getInvestments is the url for JSON file
I am getting error at this line ( var state = this.ds.getSortState();) in ext-all-debug.js file

error : this.ds is undefined

amanind
4 Apr 2011, 12:17 AM
Stefan, I am getting data in this.store but in array format and not loading on page. Page is still blank

teqneers
4 Apr 2011, 12:25 AM
You create the store as

var store = new Ext.data.Store({
...

but you assign the store using

...
store: this.store,
...
Change this to

...
store:store,
...

amanind
4 Apr 2011, 12:31 AM
Stepan, data is not loading on page, this is no error.
Can you please help me.
I will give my JSON and code

amanind
4 Apr 2011, 12:34 AM
Here is my code JS and JSON files

teqneers
4 Apr 2011, 12:39 AM
As the source code is quite messy (no line-breaks in the lower part) it's extremely hard to debug. I found one statement in the lower part that says

this.store.loadData(this.FundDetails);
If this code is really executed, you're overwriting your store's data.

amanind
4 Apr 2011, 12:48 AM
I found one thing more, Data is loading for one second and then disappear

teqneers
4 Apr 2011, 12:50 AM
There are a lot of things happening in your code so it's very hard to debug... Remove or comment out as much code as possible to keep only the bare minimum that displays the data in the grid. Add the rest of the code step by step until it doesn't work any more... This makes it relatively easy to find the problem.

teqneers
4 Apr 2011, 12:52 AM
I found one thing more, Data is loading for one second and then disappear

Did you read my next to last statement?

amanind
4 Apr 2011, 12:57 AM
Yes, but if I remove this line
this.store.loadData(this.FundDetails);

then there is no data in the store

here is clean code now



MS.ImpactSisterApp.FinancialProjectionsPanel = Ext.extend(Ext.grid.GridPanel, {
store: null,
initComponent: function() {
this.store = new Ext.data.Store({
xtype: 'store',
autoLoad: true,
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [
{ name: 'id', mapping: 'id', type: 'int' },
{ name: 'name', mapping: 'name' },
{ name: 'Sync', mapping: 'Sync', type: 'date' },
{ name: 'validate', mapping: 'validate' },
{ name: 'version', mapping: 'version' },
{ name: 'model_status', mapping: 'model_status' },
{ name: 'ev_status', mapping: 'ev_status' },
{ name: 'val_comm', mapping: 'val_comm' },
{ name: 'updated_by', mapping: 'updated_by' },
{ name: 'updated_on', mapping: 'updated_on', type: 'date' }]
}),
url: MS.ImpactSisterApp.testUrl.getInvestments
});
debugger
var config = {
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: this.store,
columns: [
{ header: 'Id', dataIndex: 'id', hidden:true },
{ header: 'Fund/Region/Investment/Model', id: 'col-name', dataIndex: 'name', treeCol: true, width: 250 },
{ header: 'Sync Status', dataIndex: 'Sync', width: 100 },
{ header: 'Validation', dataIndex: 'validate', width: 100 },
{ header: 'Version', dataIndex: 'version', width: 100 },
{ header: 'Model Status', dataIndex: 'model_status', width: 100 },
{ header: 'EV Carry Value Status', dataIndex: 'ev_status', width: 100 },
{ header: 'Val Comm Control', dataIndex: 'val_comm', width: 100 },
{ header: 'Last Updated By', dataIndex: 'updated_by', width: 100 },
{ header: 'Last Updated On', dataIndex: 'updated_on', width: 100 }
],
style: 'font-size:8pt;font-family:helvetica;overflow:auto;',
autoScroll: true,

plugins: [
new Ext.ux.grid.ColumnHeaderGroup({
rows: [
[{
colspan:5
},
{
header: 'Asset Management Review',
align: 'center'
},
{
header: 'Investment Control Review',
align: 'center'
},
{
header: 'GPG',
align: 'center'
},{
colspan:2
}]
]
})
],
closable: true
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
MS.ImpactSisterApp.FinancialProjectionsPanel.superclass.initComponent.apply(this, arguments);

//this.store.loadData(this.FundDetails);
//this.refreshInvestmentAssetModelsPanel(this);
},
onDestroy: function() {

}
});

teqneers
4 Apr 2011, 1:14 AM
The JSON data returned is unsuitable for the tree reader. The data should just start with



[{
id : 1,
name : 'MSREF I',
...
}]

amanind
4 Apr 2011, 1:45 AM
Stepan

I can not do this because I am loading json data with ajax so i have to use this



{success:true, data: {FundDetails:[{
id : 1,
...

amanind
4 Apr 2011, 1:49 AM
Stepan, i got the solution.
I have change the read method in treeReader and it works

thanks alot for your help

teqneers
4 Apr 2011, 2:19 AM
Glad you made it work...

But by the way: nothing requires you to have the JSON data format you described above. It all depends on the reader extracting the data from the JSON response. Your response format must match the format the reader requires - that's the only constraint. But it's absolutely OK to modify the reader in your case (if the data is used somewhere else as well).

amanind
5 Apr 2011, 2:08 AM
I want to call "expandAll" and "collapseAll" methods
How to do that?

teqneers
5 Apr 2011, 2:11 AM
grid.getView().expandAll();
grid.getView().collapseAll();

amanind
5 Apr 2011, 2:26 AM
I want to call it outside the class



if(rec.data.id == 99999){
return '<alert href="#" onclick="Ext.ux.tree.GridView().expandAll()">'+rec.data.name+'</a>';
} else {
return rec.data.name;
}

teqneers
5 Apr 2011, 2:56 AM
That doesn't work. You have to call the methods via the instance of your grid. The easiest way would be to assign an id-property in your grid and use Ext.getCmp() to retrieve the instance.

amanind
5 Apr 2011, 3:49 AM
Thanks Stepan for your help, it works with Ext.getCmp

wdeqing
5 Apr 2011, 7:18 AM
Hi,

It is very nice that I am able to add an editor such as editor:{ xtype: 'datefield' }. But after the date change, the icon for that row is gone and the text is aligned to the left end. Do you see this problem? Can you advice how to fix it?

Thank you in advance,

Deqing

wdeqing
5 Apr 2011, 7:41 AM
I attached my testing code below. You can see the problem by either change Cost value or eDate value.

var mytree = new Ext.grid.EditorGridPanel({
renderTo: Ext.getBody(),
autoHeight: true,
width: 400,
columnLines: true,
autoExpandColumn: 'col-name',
view: new Ext.ux.tree.GridView({
useArrows: true,
staticTree: false
}),
store: {
xtype: 'store',
autoDestroy: true,
reader: new Ext.ux.tree.TreeReader({
fields: [{
name: 'id',
mapping: 'id',
type: 'int'
}, {
name: 'name',
mapping: 'name'
}, {
name: 'edate',
mapping: 'edate'
}, {
name: 'cost',
mapping: 'cost',
type: 'float'
}]
}),
data: [{
id: 1,
name: 'Company A',
cost: 1000000.00,
edate: '2011-03-30',
leaf: false,
expanded: true,
children: [{
id: 11,
name: 'Department AA',
cost: 800000.00,
edate: '2011-03-30',
leaf: false,
expanded: true,
children: [{
id: 111,
name: 'Thing AAA',
cost: 300000.00,
edate: '2011-03-30',
leaf: true
}, {
id: 112,
name: 'Thing AAB',
cost: 500000.00,
edate: null,
leaf: true
}]
}, {
id: 12,
name: 'Department AB',
cost: 200000.00,
edate: '2011-03-30',
leaf: false,
expanded: true,
children: [{
id: 121,
name: 'Thing ABA',
cost: 50000.00,
edate: '2011-03-30',
leaf: true
}, {
id: 122,
name: 'Thing ABB',
cost: 150000.00,
edate: null,
leaf: true
}]
}]
}, {
id: 2,
name: 'Company B',
cost: 200000.00,
edate: '2011-03-31',
leaf: false,
expanded: true,
children: [{
id: 21,
name: 'Department BA',
cost: 100000.00,
edate: '2011-03-30',
leaf: false,
expanded: true,
children: [{
id: 211,
name: 'Thing BAA',
cost: 50000.00,
edate: '2011-03-30',
leaf: true
}, {
id: 212,
name: 'Thing BAB',
cost: 50000.00,
edate: '2011-03-30',
leaf: true
}]
}, {
id: 22,
name: 'Department BB',
cost: 100000.00,
edate: '2011-03-30',
leaf: false,
expanded: true,
children: [{
id: 221,
name: 'Thing BBA',
cost: 90000.00,
edate: '2011-03-30',
leaf: true
}, {
id: 222,
name: 'Thing BBB',
cost: 10000.00,
edate: '2011-03-30',
leaf: true
}]
}]
}]
},
columns: [{
header: 'Id',
dataIndex: 'id',
width: 30
}, {
header: 'Name',
id: 'col-name',
dataIndex: 'name',
treeCol: true
}, {
header: 'eDate',
dataIndex: 'edate',
width: 80,
editor: {
xtype: 'datefield'
}
}, {
header: 'Cost',
dataIndex: 'cost',
width: 80,
editor: {
xtype: 'textfield'
}
}]
});

wdeqing
6 Apr 2011, 3:45 AM
I got this problem when I use extjs 3.3.1. But it is ok now when I switched to extjs 3.2.1.

teqneers
6 Apr 2011, 4:20 AM
There have been some major internal changes on how a grid view would render its items. The tree-grid-view presented in this thread is incompatible with this new rendering and won't work correctly with ExtJS 3.3.1 and up. We do have a working version for ExtJS 3.3.1 but it still needs some testing and can not be released currently.

wdeqing
6 Apr 2011, 4:48 AM
Thanks teqneers for the info!

wdeqing
7 Apr 2011, 7:55 PM
Is there any way to display a different icon for the tree? I am expecting to specify such as "icon":"link.gif" in JSON data to display the icon. But it does not work to me. Please help. Thanks.

teqneers
7 Apr 2011, 10:49 PM
Set the iconCls property to a CSS class that defines the icon to be displayed. It's not possible to set the icon image directly.

Alternatively you can include the HTML to render the icon into the record property you defined for the tree column.

wdeqing
8 Apr 2011, 3:29 AM
great. Thanks teqneers for your advice!

amanind
11 Apr 2011, 6:50 AM
Hi Stepan,

In my Grid there are around 1000 rows and with large number of rows expandAll and collapseAll does not work and even script performance reduce to too low while rendering grid. Any solution for this?

Thanks
aMaN

teqneers
11 Apr 2011, 8:49 AM
Hi Aman,

that's quite difficult to handle because the component as presented here does not support dynamic loading or successive rendering. To implement these features a lot of core code has to be rewritten and that's currently not on our scope (mainly because we use the grid for rather small trees < 50 items).
Sorry to say that but the component is not supposed to handle record sets that big.

But you could try to implement successive rendering and share your ideas here in the forum.

Best regards

Stefan

amanind
11 Apr 2011, 11:35 PM
Thanks Stepan

Now I am trying to use Ext.ux.tree.TreeGrid.
Can you help me in this. Data is not loading in grid.

here is my code and JSON



MS.ImpactSisterApp.FinancialProjectionsPanel = Ext.extend(Ext.Panel, {
store: null,
grid2:null,
initComponent: function() {
//debugger
Ext.QuickTips.init();
this.store = new Ext.data.Store({
autoLoad: true,
autoDestroy: true,
data:[],
reader: new Ext.data.JsonReader({
fields: [
{ name: 'id', mapping: 'id', type: 'int' },
{ name: 'name', mapping: 'name' },
{ name: 'Sync', mapping: 'Sync', type: 'date' },
{ name: 'validate', mapping: 'validate' },
{ name: 'version', mapping: 'version' },
{ name: 'model_status', mapping: 'model_status' },
{ name: 'ev_status', mapping: 'ev_status' },
{ name: 'val_comm', mapping: 'val_comm' },
{ name: 'updated_by', mapping: 'updated_by' },
{ name: 'updated_on', mapping: 'updated_on', type: 'date' }]
})
});
grid2 = new Ext.ux.tree.TreeGrid({
enableDD: true,
id : 'grid1',
store: this.store,
columns: [
{ header: 'Id', dataIndex: 'id', hidden:true },
{ header: 'Fund/Region/Investment/Model', id: 'name', dataIndex: 'name', width: 250, minWidth: 250 },
{ header: 'Sync Status', dataIndex: 'Sync', width: 100, minWidth: 100 },
{ header: 'Validation', dataIndex: 'validate', width: 100, minWidth: 100 },
{ header: 'Version', dataIndex: 'version', width: 100, minWidth: 100 },
{ header: 'Model Status', dataIndex: 'model_status', width: 200, minWidth: 200, align: 'center' },
{ header: 'EV Carry Value Status', dataIndex: 'ev_status', width: 200, minWidth: 200, align: 'center' },
{ header: 'Val Comm Control', dataIndex: 'val_comm', width: 200, minWidth: 200, align: 'center' },
{ header: 'Last Updated By', dataIndex: 'updated_by', width: 100, minWidth: 100 },
{ header: 'Last Updated On', dataIndex: 'updated_on', width: 100, minWidth: 100 }
],
stripeRows: true,
iconCls:'icon-grid',
height: 510,
frame:true/*
});

var config = {
frame: true,
timeout: 60000,
hideBorders: true,
header: false,
layout: 'table',
title: "Workflow dashboard",
items:[ grid2 ],
closable: true
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
MS.ImpactSisterApp.FinancialProjectionsPanel.superclass.initComponent.apply(this, arguments);
//debugger
this.store.loadData(this.FundDetails);
//this.refreshInvestmentAssetModelsPanel(this);
},
refreshInvestmentAssetModelsPanel: function() {
try{
this.store.removeAll();
}catch(ex){
}
this.store.loadData(this.FundDetails);
},
onDestroy: function() {

}
});


JSON code



{success:true, data: {FundDetails:[{
id : 99999,
name : 'Collapse All/Expand All',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
leaf : true,
updated_on : ''
},
{
name:'MSREF I',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
expanded: true,
children:[{
name : 'America',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf: true,
iconCls: 'task'
}]
},{
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment3',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment4',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf: true,
iconCls: 'task'
}]
}, {
name : 'Asia',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf: true,
iconCls: 'task'
}]
},{
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment3',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment4',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf: true,
iconCls: 'task'
}]
}]
},{
name:'MSREF II',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
expanded: true,
children:[{
name : 'America',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf: true,
iconCls: 'task'
}]
},{
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment3',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment4',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf: true,
iconCls: 'task'
}]
}, {
name : 'Asia',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by:'',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Investment1',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
iconCls:'task-folder',
children:[{
name : 'Model1',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model2',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model3',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf:true,
iconCls:'task'
},{
name : 'Model4',
Sync : '\/Date(1254283200000-0400)\/',
validate : 'Ok',
version : 'Management',
model_status : 'pending',
ev_status : 'pending',
val_comm : 'checked',
updated_by : 'samandee',
updated_on : '\/Date(1254283200000-0400)\/',
leaf: true,
iconCls: 'task'
}]
},{
name : 'Investment2',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment3',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf:true,
iconCls:'task'
},{
name : 'Investment4',
Sync : '',
validate : '',
version : '',
model_status : '',
ev_status : '',
val_comm : '',
updated_by : '',
updated_on : '',
leaf: true,
iconCls: 'task'
}]
}]
}]
}
}

teqneers
11 Apr 2011, 11:43 PM
The Ext.ux.tree.TreeGrid doesn't use the Ext.data.Store model but uses an Ext.ux.tree.TreeGridLoader instead. The functionality is quite different and there is no interoperability between the two store or loader models.

That was one of the reasons we implemented our own tree component.

amanind
12 Apr 2011, 12:17 AM
Stepan, Can you help me in code.
I am not able to make it working. can you please do the changes in code

teqneers
12 Apr 2011, 4:23 AM
Hi Aman,

sorry, I'm trying to help as much as possible, but I cannot provide all-inclusive Sencha Ext JS support. Please read the relevant parts in the manual about the tree (Ext.tree.TreePanel) and the treeloader (Ext.tree.TreeLoader).
You should be able to come up with a working solution in no time - it's not that difficult to implement, but unfortunately it differs from the Ext.data.Store concept.

Best regards

Stefan

renaudham
18 Apr 2011, 2:55 AM
Hi

Do you plan to have an upgrade for EXTJS4? and is there any real good reason to do it or do you think that the new treegrid is solving your initial issues?

teqneers
18 Apr 2011, 3:13 AM
We haven't evaluated Ext JS 4 in detail yet, so currently it's unsure if we're required to port the tree grid to Ext JS 4. But as far as I could see from the documentation the Ext JS 4 implementation seems to haven been moved into the direction of the tree grid presented in this thread - which is a quite good evolution.

dizelland
20 Apr 2011, 1:37 AM
Hi!
How can i implement to tree-grid component Rowactions plugin (http://rowactions.extjs.eu/) ?

teqneers
20 Apr 2011, 7:19 AM
Don't know the Rowactions plugin, but the Ext.grid.ActionColumn should work like in a regular grid.

shevken
9 Aug 2011, 11:28 PM
After modifying the value in the field below to "Some Car", the car tree became non-collapsible. :((
I am using Ext 3.4.0. Did you encounter this? Thanks.

Kenny.

27401

teqneers
10 Aug 2011, 2:01 AM
Please see here: http://www.sencha.com/forum/showthread.php?94126-New-approach-to-a-tree-grid-component&p=588134&viewfull=1#post588134

Rendering has changed in the 3.3.1-release and the tree view is not compatible with the new rendering calls. Sorry, but unfortunately we cannot provide any solution currently because we're actually focusing on Ext 4 now.