PDA

View Full Version : Asynchronous tree loading



vicvolk
5 Sep 2012, 3:30 AM
Hi, guys! I tried to port my project from extjs 3 to extjs 4 and stumbled across one problem. I do not know why I can't implement asynchronous loading. I changed one line of code in reorder example from url: 'get-nodes.php'
to url: '../../../../../get/get_obj.php' which returns the structure of my tree in json format, but the catch is the tree is populated statically. But with initial url the example runs well. Should I do some more modifications to make it work? Here is my code



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

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

var store = Ext.create('Ext.data.TreeStore', {
proxy: {
type: 'ajax',
//initial url - content is loaded asynchronously
//url: 'get-nodes.php'

//my url - content is loaded statically
url: '.././../../../../get/get_obj.php'
},
root: {
text: 'Ext JS',
id: 'src',
expanded: true
},
folderSort: true,
sorters: [{
property: 'text',
direction: 'ASC'
}]
});

var tree = Ext.create('Ext.tree.Panel', {
store: store,
viewConfig: {
plugins: {
ptype: 'treeviewdragdrop'
}
},
renderTo: 'tree-div',
height: 300,
width: 250,
title: 'Files',
useArrows: true,
dockedItems: [{
xtype: 'toolbar',
items: [{
text: 'Expand All',
handler: function(){
tree.getEl().mask('Expanding tree...');
var toolbar = this.up('toolbar');
toolbar.disable();

tree.expandAll(function() {
tree.getEl().unmask();
toolbar.enable();
});
}
}, {
text: 'Collapse All',
handler: function(){
var toolbar = this.up('toolbar');
toolbar.disable();

tree.collapseAll(function() {
toolbar.enable();
});
}
}]
}]
});
});

tvanzoelen
5 Sep 2012, 5:07 AM
Have you set isLeaf: true on the nodes in the JSON?

vicvolk
5 Sep 2012, 5:17 AM
My json looks like this [{"id":"70110","text":"Количество мобильных групп","id_parent":"0","menu_item":"Количество мобильных групп","if_folder":"1","id_inherit":"0","if_obj":"3","children":[{"id":"70111","text":"Количество групп","id_parent":"70110","menu_item":"Количество групп","if_folder":"0","id_inherit":"0","if_obj":"0","leaf":true} etc.

So it has "leaf" property, not "isLeaf".

vicvolk
5 Sep 2012, 10:42 PM
Guys, no one knows how to implement this simplest task? In extjs 3 it was just as simple as my nose, and with this version I spent two days trying to solve the problem, but to no avail. Probably someone has a simple working example of asynchronous tree getting store from ajax call. As I said the standard example does not work. I just changed url and my tree got static. Really-really need help. Thanks in advance!

tvanzoelen
5 Sep 2012, 11:08 PM
Maybe I do not understand the question. What do you mean by asynchronic loading?

Loading all at once?

vicvolk
5 Sep 2012, 11:51 PM
To the contrary. When I click on a folder node its leaves should be uploaded on the fly.

tvanzoelen
5 Sep 2012, 11:59 PM
Then do not return all nodes. If leaf is not set it should make a call to the server. Can you see in the debuggger if that request is made?

If not maybe the leaf property is not set default to false. Return leaf: false or use a treemodel



Ext.define('YOUR.tree.Model', {
extend: 'Ext.data.Model',
fields: [
{ name: 'text', type: 'string', defaultValue: null },
{ name: 'parentId', type: 'string', defaultValue: null },
{ name: 'index', type: 'int', defaultValue: null },
{ name: 'depth', type: 'int', defaultValue: 0 },
{ name: 'expanded', type: 'bool', defaultValue: false, persist: false },
{ name: 'expandable', type: 'bool', defaultValue: true, persist: false },
{ name: 'checked', type: 'auto', defaultValue: null },
{ name: 'leaf', type: 'bool', defaultValue: false, persist: false },
{ name: 'cls', type: 'string', defaultValue: null, persist: false },
{ name: 'icon', type: 'string', defaultValue: null, persist: false },
{ name: 'iconCls', type: 'string', defaultValue: null, persist: false },
{ name: 'iconQtip', type: 'string', defaultValue: null, persist: false },
{ name: 'root', type: 'boolean', defaultValue: false, persist: false },
{ name: 'isLast', type: 'boolean', defaultValue: false, persist: false },
{ name: 'isFirst', type: 'boolean', defaultValue: false, persist: false },
{ name: 'allowDrop', type: 'boolean', defaultValue: true, persist: false },
{ name: 'allowDrag', type: 'boolean', defaultValue: true, persist: false },
{ name: 'loaded', type: 'boolean', defaultValue: false, persist: false },
{ name: 'loading', type: 'boolean', defaultValue: false, persist: false },
{ name: 'href', type: 'string', defaultValue: null, persist: false },
{ name: 'hrefTarget', type: 'string', defaultValue: null, persist: false },
{ name: 'qtip', type: 'string', defaultValue: null, persist: false },
{ name: 'qtitle', type: 'string', defaultValue: null, persist: false }
]
});

vicvolk
6 Sep 2012, 12:16 AM
Thanks! I will check it soon!!!

vicvolk
6 Sep 2012, 12:53 AM
Now my json looks like this. I explicitly set leaf property - either true or false. But still the tree is loaded totally as it was a static tree.



[{"id":"70327","text":"Шаблон запроса","id_parent":"0","menu_item":"Шаблон запроса","if_folder":"1","id_inherit":"0","if_obj":"3","leaf":false,"children":[{"id":"70332","text":"Запрос за I квартал","id_parent":"70327","menu_item":"Запрос за I квартал","if_folder":"1","id_inherit":"0","if_obj":"2","leaf":false,"children":[{"id":"70336","text":"Номер п/п","id_parent":"70332","menu_item":"Номер п/п","if_folder":"0","id_inherit":"70331","if_obj":"0","leaf":true},{"id":"70333","text":"Поле 1","id_parent":"70332","menu_item":"Поле 1","if_folder":"0","id_inherit":"70328","if_obj":"0","leaf":true}


When I worked with extjs 3, ajax also returned the full tree with all nodes and leaves, but setting tree loader and tree root it all worked as expected.

My previous tree declaration (extjs 3) had this lines of code which now became deprecated



...
loader: new Ext.tree.TreeLoader({

dataUrl : "./get/get_obj.php",
requestMethod : "GET",
preloadChildren:true
}),
root:new Ext.tree.AsyncTreeNode({
expanded: true,
text: ''
})
...


Still need help ((

tvanzoelen
6 Sep 2012, 12:55 AM
Do not use the TreeLoader but set the TreeStore, proxy, reader on the TreePanel.

Just like it is done with gridpanels

vicvolk
6 Sep 2012, 12:56 AM
So am I right, that in extjs 4 the previous ajax call that was working with extjs 3 should be significantly modified? I thought that there would be only some modifications concerning tree declaration.

vicvolk
6 Sep 2012, 12:57 AM
I do not use TreeLoader now - it was an extjs 3 version. Now I use Treestore etc. as you say

vicvolk
6 Sep 2012, 1:52 AM
So, I have this minified code for my tree:



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

Ext.onReady(function() {

Ext.define('Task', {
extend: 'Ext.data.Model',
fields: [
{name: 'task', type: 'string'}
],
proxy: {
type: 'ajax',
url: 'treegrid.json'
}
});

var treeStore = Ext.create('Ext.data.TreeStore', {
model: 'Task',
folderSort: true
});


var tree = Ext.create('Ext.tree.Panel', {
title: 'Test tree',
width: 700,
height: 500,
renderTo: Ext.getBody(),
collapsible: true,
rootVisible: false,
store: treeStore,
multiSelect: true,
singleExpand: false,
loadMask: true,
columns: [{
xtype: 'treecolumn',
text: 'Task',
flex: 2,
sortable: true,
dataIndex: 'task'
}]
});
});



And here goes my json file:



{"text":".","children": [
{
task:'Project: Shopping',
leaf:false,
children:[{
task:'Housewares',
leaf:false,
children:[{
task:'Kitchen supplies',
leaf:true
},{
task:'Groceries',
leaf:true
},{
task:'Cleaning supplies',
leaf:true
},{
task: 'Office supplies',
leaf: true
}]
}, {
task:'Remodeling',
leaf:false,
children:[{
task:'Retile kitchen',
leaf:true
},{
task:'Paint bedroom',
leaf:false,
children: [{
task: 'Ceiling',
leaf: true
}, {
task: 'Walls',
leaf: true
}]
},{
task:'Decorate living room',
leaf:true
},{
task: 'Fix lights',
leaf: true
}, {
task: 'Reattach screen door',
leaf: true
}]
}]
},{
task:'Project: Testing',
leaf:false,
children:[{
task: 'Mac OSX',
leaf:false,
children: [{
task: 'FireFox',
leaf: true
}, {
task: 'Safari',
leaf: true
}, {
task: 'Chrome',
leaf: true
}]
},{
task: 'Windows',
leaf:false,
children: [{
task: 'FireFox',
leaf: true
}, {
task: 'Safari',
leaf: true
}, {
task: 'Chrome',
leaf: true
},{
task: 'Internet Exploder',
leaf: true
}]
},{
task: 'Linux',
leaf:false,
children: [{
task: 'FireFox',
leaf: true
}, {
task: 'Chrome',
leaf: true
}]
}]
}
]}


And the tree is loading statically. So what modifications should be done to make it upload child leaves on the fly? See, that json contains the full structure of my tree (as it was in my extjs 3 version, that worked correctly) and nevertheless I do not want to render it at once. Not until the user clicks on the parent node should appear its children. Probably, I should form json response by PHP and on each call I should return children of some particular node? Extjs 3 did not have this logic, if I'm not mistaken, since my tree was working perfectly.

tvanzoelen
6 Sep 2012, 2:02 AM
So I assume you want to load the children on the fly when a parent node is clicked/expanded from the server and render them on the tree.

Then do not return any children in you json. Only root nodes. If these nodes are expanded or clicked it will request the server for its children. And your server must provide only the childnodes of the parent nodeid that is sent.

vicvolk
6 Sep 2012, 2:14 AM
Thank you, tvanzoelen!! But I guess that it is not all about returning json. On the first call when my page is rendered for the first time I should return only parent node of the "first" level and no children at all. And that is what will be done by setting my proxy


proxy: {
type: 'ajax',
url: 'get-nodes.php'
}


but if I want to upload children I should use some listener which will fire another ajax-call and upload children of a particular node? This way? I guess no, because it is not very effective to upload children every time when a user clicks on a parent node. I have a hope that there is some more easy and practical way to handle this problem.

Thanks for trying to help!

vicvolk
6 Sep 2012, 2:19 AM
Still, thank you, tvanzoelen! I will mark your answer, as I see what you mean and what should be done.

vicvolk
6 Sep 2012, 2:43 AM
You was 100% right! Many thanks! At once I implemented this task. The catch was to specify node:id property of proxy and write a specific server-side code which returns particular nodes depending on the id.

If someone has the same problem I recommend this thread http://apexluis.blogspot.com/2011/07/extjs-4-treepanel-php-mysql-simple.html

slientevil
12 Jan 2013, 11:29 PM
Hi I`m a Chinese I`m learning Ext4, I meet some problems just like yours, so I hope I can make myself clear.
It is about the PHP, Could you tell me about how dose the PHP response a JSON file to the client, and let the Ext treePanel to show it. I just want to know the code in PHP, Thank you in advance!!!

vicvolk
14 Jan 2013, 6:46 AM
Hi, slientevil! I do it like this:

Firstly, my tree store:





treeStore = Ext.create('Ext.data.TreeStore', {
autoload:false,
id:'treeStore',
model: 'Field',
proxy: {
type: 'ajax',
url: './get/get_obj.php', // PHP-code that generates the answer
node: 'id' // Be sure to specify this parameter
},
root: {
text: 'Objects',
id: 'src',
expanded: true
}
});




And this is the bit of my PHP code, that generates answer:





$parent_id = $_GET['node']; // we get that specific parameter

$query = "SELECT * from mytree where id_parent=".$parent_id;
$rs = mysql_query($query);
$arr = array();
while($obj = mysql_fetch_object($rs)) {

///some extra code of mine

$arr[] = $obj;

}

echo php_json_encode($arr); // function that produces json code


// Here it is


function php_json_encode($a = false){

if (is_null($a)) return 'null';
if ($a === false) return 'false';
if ($a === true) return 'true';

if (is_scalar($a)){

if (is_float($a)){

return floatval(str_replace(",", ".", strval($a)));

}

if (is_string($a)){

static $jsonReplaces = array(array("\n", "\t", "\r", "\b", "\f", '"'), array('\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"';

}

else return $a;

}

$isList = true;

for ($i = 0, reset($a); $i < count($a); $i++, next($a)){

if (key($a) !== $i){

$isList = false;
break;

}
}

$result = array();

if ($isList){

foreach ($a as $v) $result[] = php_json_encode($v);
return '[' . join(',', $result) . ']';

}
else {

foreach ($a as $k => $v) $result[] = php_json_encode($k).':'.php_json_encode($v);
return '{' . join(',', $result) . '}';
}
}



Every time, the user clicks on the tree node, te server produces the get-call like this:

http://diplomat/get/get_obj.php?node=73854


And finally, depending on that node, the server returns json code. For the first time, before the user clicked on the tree the very same sql-query selects all fileds, that have id_parent = 0, that is all folder of the highest level.

slientevil
14 Jan 2013, 11:28 PM
Thank you vicvolk! You really do me a big favour! It works well!