PDA

View Full Version : Complex JSON into TreePanel - General approach questions



myExtJsUname
11 Oct 2012, 6:46 AM
Hello all,

For the last couple of days I've been trying to figure out how to display the JSON response of a service in a TreePanel. In trying to figure out how to accomplish this, I find I have some questions regarding a general approach to doing so that I would appreciate someone commenting on when they have a moment.

First, the background...

There is an existing service (that I cannot edit) that returns a JSON containing all the information I need to create the tree I want. Unfortunately, it does not provide the ability to ask for individual levels in the tree as I see many of the examples doing. It does, however, enable me to determine whether a given "node" is a "folder" or a "leaf" (by virtue of the existence of a "subLayerIds" array) and what its parent is (through a "parentLayerId" where -1 indicates its parent is "root"). An example of this JSON can be seen here:


[
{
"id": 0,
"name": "Utilities",
"parentLayerId": -1,
"subLayerIds": [
1,
2
]
},
{
"id": 1,
"name": "Power Lines",
"parentLayerId": 0,
"subLayerIds": null
},
{
"id": 2,
"name": "Pipeline",
"parentLayerId": 0,
"subLayerIds": null
},
{
"id": 3,
"name": "Agriculture",
"parentLayerId": -1,
"subLayerIds": [
4,
8
]
},
{
"id": 4,
"name": "Wheat",
"parentLayerId": 3,
"subLayerIds": [
5,
6,
7
]
},
{
"id": 5,
"name": "Straw Yield (Oven Dried Tonnes per Hectare)",
"parentLayerId": 4,
"subLayerIds": null
},
{
"id": 6,
"name": "Available Straw (Oven Dried Tonnes)",
"parentLayerId": 4,
"subLayerIds": null
},
{
"id": 7,
"name": "Available Straw Less Cattle Usage (Oven Dried Tonnes)",
"parentLayerId": 4,
"subLayerIds": null
},
{
"id": 8,
"name": "Barley",
"parentLayerId": 3,
"subLayerIds": [
9,
10,
11
]
},
:


Second, what I've tried...

I have tried to apply what I've seen in the examples, but haven't been able to get things to work fully given the limitations of the service responses. For example, using an Ext.data.TreeStore shows me that, by the time it gets to the "append" method, it's already added a "node" at the root whether it should be there or not. I haven't figured out where (or if) I can interrupt the process flow before the append occurs to remove those nodes that shouldn't be placed at the root. I find I can remove them during the "load", but then there is also the problem of doing the "load" on a "folder" expansion and no longer having the data, therefore needing to re-request and remove everything whose parent isn't the folder in question, etc. As I've been trying to work my way through this, I'm realizing it is not the way to go about it, but at least trying to do so has afforded me some exposure to these sections of ExtJs and I've learned enough to at least be able to ask a question.

Finally, my questions...

I'm wondering if a better way to approach this might be to simply use an Ext.data.Store to get the JSON from the service, parse through it to build an entire "children" array (that is, the text, leaf and id values) and then place that "children" array directly into an Ext.data.TreeStore which would then be used in an Ext.tree.Panel. Since the information is not dynamic, is that a good enough way to go about this or is there a better way that utilizes portions of the ExtJs Tree functionality that I have still not come across or, more likely, not understood?

References to documentation or examples that you think would prove helpful in helping me to determine the best approach here are welcome.

Cheers
jtm

vietits
11 Oct 2012, 5:55 PM
Below are some solutions for your problem:
1. Define a custom reader that extends Ext.data.reader.Json and write your own readRecords() method to convert data to the correct form.


Ext.onReady(function(){
Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias : 'reader.myreader',
readRecords: function(data){
return this.callParent([convertData(data)]);
}
});


var treeStore = Ext.create('Ext.data.TreeStore', {
autoLoad: true,
root: {
name: 'Root'
},
fields: ['id', 'name'],
proxy: {
type: 'ajax',
url: 'index.json',
reader: {
type: 'myreader'
}
}
});


var treePanel = Ext.create('Ext.tree.Panel', {
renderTo: Ext.getBody(),
store: treeStore,
width: 500,
height: 300,
columns: [{
xtype: 'treecolumn',
header: 'Name',
flex: 1,
dataIndex: 'name'
}]
});
});

2. Use Ext.Ajax.request() to load json data, then convert it into tree that TreeStore understand, then supply the result to TreeStore.


Ext.onReady(function(){
var treeStore = Ext.create('Ext.data.TreeStore', {
fields: ['id', 'name'],
proxy: 'memory'
});


Ext.Ajax.request({
url: 'index.json',
success: function(response){
var data = Ext.decode(response.responseText);
treeStore.setRootNode({
name: 'Root',
children: convertData(data)
});
}
});


var treePanel = Ext.create('Ext.tree.Panel', {
renderTo: Ext.getBody(),
store: treeStore,
width: 500,
height: 300,
columns: [{
xtype: 'treecolumn',
header: 'Name',
flex: 1,
dataIndex: 'name'
}]
});
});

Both above examples use the same following:
1. convertData(): Here I assume that subLayerIds contains list of array indices.


function convertData(data){
var treeData = [];


function getChildren(childIdx){
var results = [];
if(childIdx){
for(var idx = 0, len = childIdx.length; idx < len; idx++){
var record = data[childIdx[idx]];
if(record){
var children = getChildren(record.subLayerIds);
results.push({
id: record.id,
name: record.name,
children: children,
leaf: children.length < 1
});
}
}
}
return results;
}


for(var idx = 0, len = data.length; idx < len; idx++){
var record = data[idx];
if(record.parentLayerId == -1){
var children = getChildren(record.subLayerIds);
treeData.push({
id: record.id,
name: record.name,
children: children,
leaf: children.length < 1
});
}
}
return treeData;
}

2. index.json


[{
"id": 0,
"name": "Utilities",
"parentLayerId": -1,
"subLayerIds": [
1,
2
]
},{
"id": 1,
"name": "Power Lines",
"parentLayerId": 0,
"subLayerIds": null
},{
"id": 2,
"name": "Pipeline",
"parentLayerId": 0,
"subLayerIds": null
},{
"id": 3,
"name": "Agriculture",
"parentLayerId": -1,
"subLayerIds": [
4,
8
]
},{
"id": 4,
"name": "Wheat",
"parentLayerId": 3,
"subLayerIds": [
5,
6,
7
]
},{
"id": 5,
"name": "Straw Yield (Oven Dried Tonnes per Hectare)",
"parentLayerId": 4,
"subLayerIds": null
},{
"id": 6,
"name": "Available Straw (Oven Dried Tonnes)",
"parentLayerId": 4,
"subLayerIds": null
},{
"id": 7,
"name": "Available Straw Less Cattle Usage (Oven Dried Tonnes)",
"parentLayerId": 4,
"subLayerIds": null
},{
"id": 8,
"name": "Barley",
"parentLayerId": 3,
"subLayerIds": [
9,
10,
11
]
}]

myExtJsUname
12 Oct 2012, 10:09 AM
vietits,

Thank you, yet again, for your response to my queries. You have definitely gone above and beyond my expectations in this particular case.

Your first suggestion is what I was trying to figure out how to accomplish and didn't recognize that I should have been focusing on the Reader to do the conversion I needed.

While I was waiting, I implemented the equivalent to your second suggestion and got it working, but will revisit the Reader idea as I prefer that approach to this particular problem.

Cheers,
jtm