PDA

View Full Version : Problem Loading Child Nodes of the Tree



Lionlancer
28 Mar 2012, 2:41 AM
Hello everyone!

I'm been struggling with this for problem a few days now. I've been doing research and it seems no one has tried what I'm trying to do.

I'm trying to display a tree which loads JSON formatted data with HATEOAS in it. The tree doesn't display right if I don't tweak the TreeStore. It only display a folder with no name. If I open the folder to see the child nodes, it'll just reload the same proxy and thus another folder with blank name is displayed. It just looping. I modified the TreeStore by adding a listener so that I can tell it what should be the content of the tree and the name of the nodes. It works but I still have a problem... Whenever I open one of the nodes, it'll load the same proxy and I will see this error in firebug:

records[i] is undefined



chrome://firebug/content/blank.gif
ns[i].viewRecordId = records[i].internalId;ext-all-debug.js (line 60517)





After I click the same node again (to close it), the node will move to the right (to indicate that it is a child and not a root anymore).

Here are my codes: NOTE: I was working in a Java environment but I only need to work on the javascript part of the site (or the front-end). The back-end (REST and other stuffs) are handled by my client.

index.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ZZZ Core Application</title>
<link rel="SHORTCUT ICON" href="resources/images/icons/favicon.ico">
<link href="js/ext-js/resources/css/ext-all.css" type="text/css" rel="stylesheet"><script src="js/ext-js/ext-all-debug.js" type="text/javascript">
<script src="js/zzz/Application.js" type="text/javascript">
<script type="text/javascript" src="js/zzz/view/Viewport.js">
<script type="text/javascript" src="js/zzz/controller/NavigatorController.js">
<script type="text/javascript" src="js/zzz/view/navigator/Tree.js">
<script type="text/javascript" src="js/zzz/store/NamespacesStore.js">
</head>
<body>
</body>
</html>

NOTE: all files are loaded properly

Application.js


Ext.onReady(function(){
Ext.Loader.setConfig({enabled:true, 'disableCaching':false}); //Enable Dynamic Class Loading
// Instantiate Application Class for ZZZ.
Ext.application({
name: 'ZZZ', //Global Variable pointing to ZZZ Client app
appFolder: 'js/zzz', //Directory path to app
controllers: ['NavigatorController'],

autoCreateViewport: true, //Create viewport
});
});


NavigatorController.js


Ext.define('ZZZ.controller.NavigatorController', {
extend: 'Ext.app.Controller',
models: ['Namespace'], //Specify models to be used
stores: ['NamespacesStore'], //Specify stores to be used
views: ['navigator.Tree', 'Viewport'] //Specify views to be used
});


Namespace.js


Ext.define('ZZZ.model.Namespace', {
extend: 'Ext.data.Model',

fields: ['id', 'name', 'type', 'description', 'created', 'createdby', 'modified', 'modifiedby', 'locked']
}

NOTE: Where the hell did the config for the proxy went? I moved it to the NamespacesStore.js because if I don't move it, it doesn't autoload the data.

NamespacesStore.js


Ext.define('ZZZ.store.NamespacesStore', {
extend:'Ext.data.TreeStore',
autoLoad:true,
storeId: 'NamespacesStore',
model:'Namespace',
// this constructor fixes the autoload of data but it seems it can't detect the Namespace model
constructor: function() {

config = Ext.apply(this,{
proxy: {
type: 'ajax', // it should be 'rest', but for the sake of this sample I'll use 'ajax'
url: 'namespaces.json',
reader: {
type: 'my-json',
//root: 'nodes'
},
headers: {
accept:'application/zzz.nav+json'
}
},
// I'm trying to modify the loaded data so that I can display it properly to the tree
listeners: {
// Each Namespace Model instance will be automatically
// decorated with methods/properties of Ext.data.NodeInterface
// (i.e., a "node"). Whenever a Namespace node is appended
// to the tree, this TreeStore will fire an "append" event.
append: function( thisNode, newChildNode, index, eOpts ) {
var nCN;

nCN = newChildNode.raw;

if( !newChildNode.isRoot() ) {
// I had to do this, I don't know why 'nCN' is missing if I don't do this check

// setting the name of the node because it's blank
newChildNode.set('text', nCN.text);

// I'm trying to set up a different proxy for each node (to load its children) because the data comes from a different url. But it doesn't work... it still uses the proxy defined above.
newChildNode.setProxy({
type: 'rest',
url: nCN.link,
reader: {
type: 'json',
//root: 'nodes'
},
headers: {
accept:'application/zzz.nav+json'
}
});
}
}

}
});

this.callParent(arguments);

}
});


Setting up my own JSON reader to be used above


Ext.define('MyReader', {
extend: 'Ext.data.reader.Json',
alias: 'reader.my-json',
read: function(object) {
var object2 = new Object();
var rT;
rT = Ext.decode(object.responseText);

// reformat object into
// {text: '.', expanded: true, children: [{...},{...}]}
object2.text = '.';
object2.expanded = true;
object2.children = new Array();

Ext.Array.forEach(rT.nodes.node, function(item, index, allItems){
var child = new Object();

child.text = item.name; // node name
child.id = item.id; // node id

Ext.Array.each(item.links.link, function(item2, index2, allItems2){
if(item2.rel == 'classes'){
child.link = item2.href; // the proxy link to use when you click a node
}
});

object2.children[object2.children.length] = child;
});

return this.callParent([object2]);
}
});


Here's the sample Namespace.json file


{"nodes": {"links": {"link": [{"rel": "self",

"href": "http://zzz/namespaces (http://localhost:9999/sbe-core/services/rest/namespaces)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "All Namespaces",

"description": "Gets every namespace as a node."

}


"node": [{"type": "NSP",

"id": "NSP-3-AERO",

"modified": "2009-02-20T09:45:00.0",

"modifiedby": "asduio",

"created": "2009-02-20T09:45:00.0",

"createdby": "asduio",

"locked": false,

"links": {"link": [{"rel": "self",

"href": "http://zzz/namespaces/NSP-3-AERO (http://localhost:9999/sbe-core/services/rest/namespaces/NSP-3-AERO)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "Namespace",

"description": "Gets namespace 'Aerospace' as a node."

},{"rel": "classes",

"href": "http://zzz/classes?namespace=NSP-3-AERO (http://localhost:9999/sbe-core/services/rest/classes?namespace=NSP-3-AERO)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "Classes",

"description": "Get each class lifecycle within the namespace as a node.'Aerospace'"

}


]

"name": "Aerospace",


"description": "Aerospace domain."

},{"type": "NSP",

"id": "NSP-2",

"modified": "2010-01-01T00:00:01.0",

"modifiedby": "admin",

"created": "2010-01-01T00:00:01.0",

"createdby": "admin",

"locked": false,

"links": {"link": [{"rel": "self",

"href": "http://zzz/namespaces/NSP-2 (http://localhost:9999/sbe-core/services/rest/namespaces/NSP-2)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "Namespace",

"description": "Gets namespace 'ZZZ' as a node."

},

{"rel": "classes",

"href": "http://zzz/classes?namespace=NSP-2 (http://localhost:9999/sbe-core/services/rest/classes?namespace=NSP-2)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "Classes",

"description": "Get each class lifecycle within the namespace as a node.'ZZZ'"

}

]

},

"name": "ZZZ",

"description": "Default entities that make up the zzz application."

}
]
}
}






NOTE: Due to the nature of this project, I modified the URLs please adjust according to your environment if you want to test this code.

Here's another sample data for the children of NSP-2 (or ZZZ)


{"nodes": {"links": {"link": [{"rel": "self",
"href": "http://zzz/classes?namespace=NSP-2 (http://localhost:9999/sbe-core/services/rest/classes?namespace=NSP-2)",

"method": "GET",

"produces": "application/zzz.nav+json",

"title": "All Class Lifecycles",

"description": "Gets every classlifecycle within namespace 'NSP-2' as a node."

}

]

},

"node": [{"type": "CLL",

"id": "CLL-21",

"modified": "2010-01-01T00:00:01.0",

"modifiedby": "admin",

"created": "2010-01-01T00:00:01.0",

"createdby": "admin",

"locked": false,

"name": "Entity"

},

{"type": "CLL",

"id": "CLL-211",

"modified": "2010-01-01T00:00:01.0",

"modifiedby": "admin",

"created": "2010-01-01T00:00:01.0",

"createdby": "admin",

"locked": false,

"name": "Domain Entity"

},

{"type": "CLL",

"id": "CLL-2111",

"modified": "2010-01-01T00:00:01.0",

"modifiedby": "admin",

"created": "2010-01-01T00:00:01.0",

"createdby": "admin",

"locked": false,

"name": "Product Entity"

}
]



}

}

NOTE: Due to the nature of this project, I modified the URLs please adjust according to your environment if you want to test this code.

Tree.js


Ext.define('ZZZ.view.navigator.Tree', {
extend: 'Ext.tree.Panel',
alias: 'widget.navigator-tree',
store: 'NamespaceStore',
rootVisible:false,
initComponent: function(){
// added this to get the store because the view can't find it right away
this.store = Ext.data.StoreManager.lookup('NamespacesStore');
this.callParent(arguments);
}
});


Viewport.js


Ext.define('ZZZ.view.Viewport', {
extend: 'Ext.container.Viewport',
layout: 'border',

items: [{
region: 'north',
xtype: 'panel',
bodyStyle: 'padding:15px',
html : 'Welcome to the application'
}, {
region: 'west',
title: 'Navigator',
width: '25%',
collapsible:true,
xtype: 'navigator-tree',
rootVisible: false,
bodyStyle: 'padding:0px'
// add TreePanel
}, {
region: 'center',
width: '75%',
bodyStyle: 'padding:0px',
layout: {
type: 'border'
},
defaults: {
split:true,
bodyStyle: 'padding:0px'
},
items : [ {
region: 'north',
title: 'Manager',
height: '50%',
collapsible:true,
}, {
region: 'center',
title: 'Editor',
collapsible:false,
layout: 'fit',
html: 'Editor',
bodyStyle: 'padding:15px',
xtype: 'panel'
}]
}, {
region: 'south',
xtype: 'panel',
bodyStyle: 'padding:15px',
html : ''
}],
renderTo: document.body

});


I'm just new to ExtJs and the code above are the product of my researches and reading tutorials. I hope you can help me solve this problem.

Thanks a lot guys!

Lionlancer
29 Mar 2012, 12:41 AM
Really? No one can answer this?
Ok, I'll make it more simple...

Is it possible for the child node to overwrite the root node's proxy? For example, I have this tree:


+ Item1
+ Item2
+ Item3
+ Item4


They were loaded using this URL: http://localhost/project/items?node=root (note: i removed the cache buster part and this is just a sample link)
There's no config for root and rootVisible: false

What I want is if I expand Item1, it will load this URL: http://localhost/project/subitems?item=Item1
Item2 will load this URL: http://localhost/project/subitems?item=Item2
And so on...

Is this approach possible? I've been overwriting the child node's proxy while I'm using the Ext.data.TreeStore listener (on 'append' event). But that doesn't work.

Another problem using the tree sample above, if I expand Item1, it will load the same URL above although slightly different:
http://localhost/project/items?node=item1

It will load the same data above and I will get this error:

records[i] is undefined http://project/js/ext-js/ext-all-debug.js Line 60517

The icon (folder) beside Item1 is now in expanded state. After I click it again to collapse Item1, it will load the same URL: http://localhost/project/items?node=item1 and the tree will be like below:


- Item1
+ Item2
+ Item3
+ Item4

You will notice Item1 is expanded and in the position of the child node. The same thing will happen to other nodes.

So my other question is, what should I do to fix this?

I don't know if this is really a bug or I made a mistake somewhere while building the app.
Just look at the first post for the codes.

I really appreciate all the help I can get. I hope someone can help me.