PDA

View Full Version : 4.1.1a: Treegrid metaData fields problem



bladesling
17 Jan 2013, 12:14 PM
I can't seem to get the treegrid to accept the metaData fields parameter from a json call. I got the columns to reconfigure and the fields to assign to the model but any time the models fields get sent with the ajax call the tree breaks.

Is this a bug? Is this fixed in 4.1.3? I have extjs license already and I would be willing to pay for the upgrade to the support package to get 4.1.3 if it is confirmed to work.

Example:

Tree grid creation, model creation, and store creation


Ext.Loader.setConfig({
enabled: true
});Ext.Loader.setPath('Ext.ux', '../ux');

Ext.require([
'Ext.data.*',
'Ext.grid.*',
'Ext.tree.*',
]);

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

//we want to setup a model and store instead of using dataUrl
Ext.define('Task', {
extend: 'Ext.data.Model',
fields: [
{name: 'task', type: 'string'},
{name: 'duration', type: 'string'}
]
});

var store = Ext.create('Ext.data.TreeStore', {
model: 'Task',
proxy: {
type: 'ajax',
//the store will get the content from the .json file
url: 'treegrid.json'
},
folderSort: true,
listeners: {
'metachange': function(store, meta) {
console.log('meta_change', meta);
console.log(tree.store);
//tree.store.model.setFields(meta.store_fields);
// If I add the store to the reconfigure- things break
tree.reconfigure(null, meta.columns);

}
}
});

//Ext.ux.tree.TreeGrid is no longer a Ux. You can simply use a tree.TreePanel
var tree = Ext.create('Ext.tree.Panel', {
title: 'Core Team Projects',
width: 500,
height: 300,
renderTo: Ext.getBody(),
collapsible: true,
useArrows: true,
rootVisible: false,
store: store,
multiSelect: true,
singleExpand: true,
//the 'columns' property is now 'headers'
columns: [{
xtype: 'treecolumn', //this is so we know which column will show the tree
text: 'Task',
flex: 2,
sortable: true,
dataIndex: 'task'
},{
//we must use the templateheader component so we can use a custom tpl
xtype: 'templatecolumn',
text: 'Duration',
flex: 1,
sortable: true,
dataIndex: 'duration',
align: 'center',
//add in the custom tpl for the rows
tpl: Ext.create('Ext.XTemplate', '{duration:this.formatHours}', {
formatHours: function(v) {
if (v < 1) {
return Math.round(v * 60) + ' mins';
} else if (Math.floor(v) !== v) {
var min = v - Math.floor(v);
return Math.floor(v) + 'h ' + Math.round(min * 60) + 'm';
} else {
return v + ' hour' + (v === 1 ? '' : 's');
}
}
})
}]
});
});


JSON Code


{
"text": ".",
metaData: {
fields: [
{
name: 'task',
type: 'string'
},
{
name: 'duration',
type: 'string'
},
{
name: 'user',
type: 'string'
}
],
"columns": [
{
xtype: 'treecolumn',
//thisissoweknowwhichcolumnwillshowthetreetext: 'Task',
flex: 2,
sortable: true,
dataIndex: 'task'
},
{
//wemustusethetemplateheadercomponentsowecanuseacustomtplxtype: 'templatecolumn',
text: 'Duration',
flex: 1,
sortable: true,
dataIndex: 'duration',
align: 'center',
//addinthecustomtplfortherowstpl: Ext.create('Ext.XTemplate',
'{
duration: this.formatHours
}',
{
formatHours: function(v){
if(v<1){
returnMath.round(v*60)+'mins';
}elseif(Math.floor(v)!==v){
varmin=v-Math.floor(v);returnMath.floor(v)+'h'+Math.round(min*60)+'m';
}else{
returnv+'hour'+(v===1?'': 's');
}
}
})
},
{
text: 'AssignedTo',
flex: 1,
dataIndex: 'user',
sortable: true
}
]
},
"children": [
{
task: 'Project: Shopping',
duration: 13.25,
user: 'TommyMaintz',
iconCls: 'task-folder',
expanded: true,
children: [
{
task: 'Housewares',
duration: 1.25,
user: 'TommyMaintz',
iconCls: 'task-folder',
children: [
{
task: 'Kitchensupplies',
duration: 0.25,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task'
},
{
task: 'Groceries',
duration: .4,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task',
done: true
},
{
task: 'Cleaningsupplies',
duration: .4,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task'
},
{
task: 'Officesupplies',
duration: .2,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task'
}
]
},
{
task: 'Remodeling',
duration: 12,
user: 'TommyMaintz',
iconCls: 'task-folder',
expanded: true,
children: [
{
task: 'Retilekitchen',
duration: 6.5,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task'
},
{
task: 'Paintbedroom',
duration: 2.75,
user: 'TommyMaintz',
iconCls: 'task-folder',
children: [
{
task: 'Ceiling',
duration: 1.25,
user: 'TommyMaintz',
iconCls: 'task',
leaf: true
},
{
task: 'Walls',
duration: 1.5,
user: 'TommyMaintz',
iconCls: 'task',
leaf: true
}
]
},
{
task: 'Decoratelivingroom',
duration: 2.75,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task',
done: true
},
{
task: 'Fixlights',
duration: .75,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task',
done: true
},
{
task: 'Reattachscreendoor',
duration: 2,
user: 'TommyMaintz',
leaf: true,
iconCls: 'task'
}
]
}
]
},
{
task: 'Project: Testing',
duration: 2,
user: 'CoreTeam',
iconCls: 'task-folder',
children: [
{
task: 'MacOSX',
duration: 0.75,
user: 'TommyMaintz',
iconCls: 'task-folder',
children: [
{
task: 'FireFox',
duration: 0.25,
user: 'TommyMaintz',
iconCls: 'task',
leaf: true
},
{
task: 'Safari',
duration: 0.25,
user: 'TommyMaintz',
iconCls: 'task',
leaf: true
},
{
task: 'Chrome',
duration: 0.25,
user: 'TommyMaintz',
iconCls: 'task',
leaf: true
}
]
},
{
task: 'Windows',
duration: 3.75,
user: 'DarrellMeyer',
iconCls: 'task-folder',
children: [
{
task: 'FireFox',
duration: 0.25,
user: 'DarrellMeyer',
iconCls: 'task',
leaf: true
},
{
task: 'Safari',
duration: 0.25,
user: 'DarrellMeyer',
iconCls: 'task',
leaf: true
},
{
task: 'Chrome',
duration: 0.25,
user: 'DarrellMeyer',
iconCls: 'task',
leaf: true
},
{
task: 'InternetExploder',
duration: 3,
user: 'DarrellMeyer',
iconCls: 'task',
leaf: true
}
]
},
{
task: 'Linux',
duration: 0.5,
user: 'AaronConran',
iconCls: 'task-folder',
children: [
{
task: 'FireFox',
duration: 0.25,
user: 'AaronConran',
iconCls: 'task',
leaf: true
},
{
task: 'Chrome',
duration: 0.25,
user: 'AaronConran',
iconCls: 'task',
leaf: true
}
]
}
]
}
]
}

mitchellsimoens
21 Jan 2013, 11:12 AM
You could just provide JS and JSON code here

bladesling
21 Jan 2013, 11:34 AM
I edited the initial post to include the source instead of the live example as that is now being taken down.

bladesling
22 Jan 2013, 9:58 AM
I found the "solution". This probably will not work for every scenario.

Basically there is a bug in the treegrid code that when the metaData fields are supplied through the JSON, ExtJS automatically strips all the current fields from the model in the store and applies only the ones you give it. The problem is that it deletes 22 fields that are created on initiation of the treegrid model. You can find mention of what those are here: http://docs.sencha.com/ext-js/4-1/#!/guide/tree

My solution is to delete only fields that are set to dynamic. Dynamic is a new property on the fields that I made and handle with my metachange listener in the store. Alternatively you could add the 22 fields to your metaData fields along with your other fields. I just didn't like all that extra data.


To the code:
When creating the store I add a metachange listener. This handles adding/removing columns and models so you can have a dynamic tree grid in Extjs.


Ext.Loader.setConfig({
enabled:true
});
Ext.Loader.setPath('Ext.ux', '../ux');

Ext.require([
'Ext.data.*',
'Ext.grid.*',
'Ext.tree.*',
]);

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

//we want to setup a model and store instead of using dataUrl
Ext.define('Task', {
extend:'Ext.data.Model',
fields:[
{name:'task', type:'string'},
{name:'duration', type:'string'}
]
});

var store = Ext.create('Ext.data.TreeStore', {
model:'Task',
proxy:{
type:'ajax',
//the store will get the content from the .json file
url:'treegrid.json'
},
folderSort:true,
listeners: {
'metachange':function (store, meta) {
// Loop through the current model's fields
Ext.each(store.model.fields.items, function (n) {
// The field is dynamic
if (n != undefined && n.dynamic != undefined && n.dynamic == true) {
var found = false;
// Loop through the new model fields
Ext.each(meta.model_fields, function (g) {
if (n.name == g.name) {
found = true;
return false;
}
});
if (!found) {
// If the field wasn't found in the fields sent with the json, delete it
store.model.fields.removeAtKey(n.name);
}
}
});
// Add all the fields from the json into the model
Ext.each(meta.model_fields, function (n) {
store.model.fields.add(new Ext.data.Field(n));
})
// If I add the store to the reconfigure- things break
// Make sure you reference YOUR tree..
tree.reconfigure(null, meta.columns);
}
}
});

//Ext.ux.tree.TreeGrid is no longer a Ux. You can simply use a tree.TreePanel
var tree = Ext.create('Ext.tree.Panel', {
title:'Core Team Projects',
width:500,
height:300,
renderTo:Ext.getBody(),
collapsible:true,
useArrows:true,
rootVisible:false,
store:store,
multiSelect:true,
singleExpand:true,
//the 'columns' property is now 'headers'
columns:[
{
xtype:'treecolumn', //this is so we know which column will show the tree
text:'Task',
flex:2,
sortable:true,
dataIndex:'task'
},
{
//we must use the templateheader component so we can use a custom tpl
xtype:'templatecolumn',
text:'Duration',
flex:1,
sortable:true,
dataIndex:'duration',
align:'center',
//add in the custom tpl for the rows
tpl:Ext.create('Ext.XTemplate', '{duration:this.formatHours}', {
formatHours:function (v) {
if (v < 1) {
return Math.round(v * 60) + ' mins';
} else if (Math.floor(v) !== v) {
var min = v - Math.floor(v);
return Math.floor(v) + 'h ' + Math.round(min * 60) + 'm';
} else {
return v + ' hour' + (v === 1 ? '' : 's');
}
}
})
}
]
});

Ext.create('Ext.Button', {
text:'Click me',
renderTo:Ext.getBody(),
handler:function () {
tree.store.load();
}
});
});


The JSON: Whats important to note here is that I have changed the name from fields to model_fields. I did this so the metachange listener waiting for the metaData for fields doesn't attempt to apply the fields incorrectly. I also added a property to the model fields user field of dynamic. This specifies that the field should be deleted if it is not sent with the next JSON update. This does mean that you have to sent your models for each call regardless if they are changing.


{
"text": ".",
metaData: {
model_fields: [
{name:'task', type:'string'},
{name:'duration', type:'string'},
{name:'user', type:'string', dynamic:true}
],
"columns" : [
{
xtype:'treecolumn', //this is so we know which column will show the tree
text:'Task',
flex:2,
sortable:true,
dataIndex:'task'
},
{
//we must use the templateheader component so we can use a custom tpl
xtype:'templatecolumn',
text:'Duration',
flex:1,
sortable:true,
dataIndex:'duration',
align:'center',
//add in the custom tpl for the rows
tpl:Ext.create('Ext.XTemplate', '{duration:this.formatHours}', {
formatHours:function (v) {
if (v < 1) {
return Math.round(v * 60) + ' mins';
} else if (Math.floor(v) !== v) {
var min = v - Math.floor(v);
return Math.floor(v) + 'h ' + Math.round(min * 60) + 'm';
} else {
return v + ' hour' + (v === 1 ? '' : 's');
}
}
})
},
{
text:'Assigned To',
flex:1,
dataIndex:'user',
sortable:true
}
]
},
"children": [
{
task:'Project: Shopping',
duration:13.25,
user:'Tommy Maintz',
iconCls:'task-folder',
expanded:true,
children:[
{
task:'Housewares',
duration:1.25,
user:'Tommy Maintz',
iconCls:'task-folder',
children:[
{
task:'Kitchen supplies',
duration:0.25,
user:'Tommy Maintz',
leaf:true,
iconCls:'task'
},
{
task:'Groceries',
duration:.4,
user:'Tommy Maintz',
leaf:true,
iconCls:'task',
done:true
},
{
task:'Cleaning supplies',
duration:.4,
user:'Tommy Maintz',
leaf:true,
iconCls:'task'
},
{
task:'Office supplies',
duration:.2,
user:'Tommy Maintz',
leaf:true,
iconCls:'task'
}
]
},
{
task:'Remodeling',
duration:12,
user:'Tommy Maintz',
iconCls:'task-folder',
expanded:true,
children:[
{
task:'Retile kitchen',
duration:6.5,
user:'Tommy Maintz',
leaf:true,
iconCls:'task'
},
{
task:'Paint bedroom',
duration:2.75,
user:'Tommy Maintz',
iconCls:'task-folder',
children:[
{
task:'Ceiling',
duration:1.25,
user:'Tommy Maintz',
iconCls:'task',
leaf:true
},
{
task:'Walls',
duration:1.5,
user:'Tommy Maintz',
iconCls:'task',
leaf:true
}
]
},
{
task:'Decorate living room',
duration:2.75,
user:'Tommy Maintz',
leaf:true,
iconCls:'task',
done:true
},
{
task:'Fix lights',
duration:.75,
user:'Tommy Maintz',
leaf:true,
iconCls:'task',
done:true
},
{
task:'Reattach screen door',
duration:2,
user:'Tommy Maintz',
leaf:true,
iconCls:'task'
}
]
}
]
},
{
task:'Project: Testing',
duration:2,
user:'Core Team',
iconCls:'task-folder',
children:[
{
task:'Mac OSX',
duration:0.75,
user:'Tommy Maintz',
iconCls:'task-folder',
children:[
{
task:'FireFox',
duration:0.25,
user:'Tommy Maintz',
iconCls:'task',
leaf:true
},
{
task:'Safari',
duration:0.25,
user:'Tommy Maintz',
iconCls:'task',
leaf:true
},
{
task:'Chrome',
duration:0.25,
user:'Tommy Maintz',
iconCls:'task',
leaf:true
}
]
},
{
task:'Windows',
duration:3.75,
user:'Darrell Meyer',
iconCls:'task-folder',
children:[
{
task:'FireFox',
duration:0.25,
user:'Darrell Meyer',
iconCls:'task',
leaf:true
},
{
task:'Safari',
duration:0.25,
user:'Darrell Meyer',
iconCls:'task',
leaf:true
},
{
task:'Chrome',
duration:0.25,
user:'Darrell Meyer',
iconCls:'task',
leaf:true
},
{
task:'Internet Exploder',
duration:3,
user:'Darrell Meyer',
iconCls:'task',
leaf:true
}
]
},
{
task:'Linux',
duration:0.5,
user:'Aaron Conran',
iconCls:'task-folder',
children:[
{
task:'FireFox',
duration:0.25,
user:'Aaron Conran',
iconCls:'task',
leaf:true
},
{
task:'Chrome',
duration:0.25,
user:'Aaron Conran',
iconCls:'task',
leaf:true
}
]
}
]
}
]
}

watermark
21 Nov 2013, 12:07 PM
Be careful as the posted solution edits the prototype of the model. If you have two instances of the store on the screen, they will be sharing the same fields.