Reputation: 31
I'm having a some problem making JSonRest store and dijit.Tree with ForestModel. I've tried some combination of JsonRestStore and json data format following many tips on the web, with no success.
At the end, taking example form here http://blog.respondify.se/2011/09/using-dijit-tree-with-the-new-dojo-object-store/
I've made up this simple page (I'm using dojotolkit 1.7.2)
<!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=ISO-8859-1">
<title>Tree Model Explorer</title>
<script type="text/javascript">
djConfig = {
parseOnLoad : true,
isDebug : true,
}
</script>
<script type="text/javascript" djConfig="parseOnLoad: true"
src="lib/dojo/dojo.js"></script>
<script type="text/javascript">
dojo.require("dojo.parser");
dojo.require("dijit.Tree");
dojo.require("dojo.store.JsonRest");
dojo.require("dojo.data.ObjectStore");
dojo.require("dijit.tree.ForestStoreModel");
dojo.addOnLoad(function() {
var objectStore = new dojo.store.JsonRest({
target : "test.json",
labelAttribute : "name",
idAttribute: "id"
});
var dataStore = new dojo.data.ObjectStore({
objectStore : objectStore
});
var treeModel = new dijit.tree.ForestStoreModel({
store : dataStore,
deferItemLoadingUntilExpand : true,
rootLabel : "Subjects",
query : {
"id" : "*"
},
childrenAttrs : [ "children" ]
});
var tree = new dijit.Tree({
model : treeModel
}, 'treeNode');
tree.startup();
});
</script>
</head>
<body>
<div id="treeNode"></div>
</body>
</html>
My rest service responds the following json
{
data: [
{
"id": "PippoId",
"name": "Pippo",
"children": []
},
{
"id": "PlutoId",
"name": "Pluto",
"children": []
},
{
"id": "PaperinoId",
"name": "Paperino",
"children": []
}
]}
I've tried also with the following response (actually my final intention n is to use lazy loading for the tree)
{ data: [
{
"id": "PippoId",
"name": "Pippo",
"$ref": "author0",
"children": true
},
{
"id": "PlutoId",
"name": "Pluto",
"$ref": "author1",
"children": true
},
{
"id": "PaperinoId",
"name": "Paperino",
"$ref": "author2",
"children": true
}
]}
Neither of the two works. I see no error message in firebug. I simply see the root "Subject" on the page. Thanks to anybody could help in some way.
Upvotes: 3
Views: 8831
Reputation: 8641
your serverside returns the wrong data. Here's the quickstart jsonrest from dojo reference guide, follow the GET part.
There is a difference, since the manner of how your REST request looks (GET from browser), the serverside should 1) return an array of items or 2) return an item.
Try remove the data key as such:
[
{
"id": "PippoId",
"name": "Pippo",
"children": []
}, {
"id": "PlutoId",
"name": "Pluto",
"children": []
}, {
"id": "PaperinoId",
"name": "Paperino",
"children": []
}
]
So this will not bring lazy loading capeabilities just yet? This must be because the model has quite a complex setup in your sample of code, first the REST store
then an OBJECT another store
, then a ForestTree model
and last a Tree view
. It is fairly simple to implement what the model has to offer our store, and lets skip the double-store definition. Otherwise the objectstore.query will call reststore.query - which im not entirely certain will work.
RestStore
The Tree requires five model methods to render data as a tree:
getIdentity(object)
- Already provided by the store, and doesn't usually need to be reimplemented.mayHaveChildren(object)
- Indicates whether or not an object may have children (prior to actually loading the children). In this example, we will treat the presence of a "children" property as the indication of having children.getChildren(parent, onComplete, onError)
- Called to retrieve the children. This may execute asynchronously and should call the onComplete callback when finished. In this example, we will do a get() to retrieve the full representation of the parent object to get the children. Once the parent is fully loaded, we return the "children" array from the parent.getRoot(onItem)
- Called to retrieve the root node. The onItem callback should be called with the root object. In this example, we get() the object with the id/URL of "root" for the root object.getLabel(object)
- Returns the label for the object (this is the text that is displayed next to the node in the tree). In this example, the label is just the "name" property of the object.How could this be done then? Lets make a couple of definitions:
1) server sets for jsonrest.target
'base', the ID 'root' and 2) server returns 'children' key allways, true if there are any
var reststore = JsonRest({
target:"data/", // << adapt URL
mayHaveChildren: function(object){
// if true, we might be missing the data, false and nothing should be done
return "children" in object;
},
getChildren: function(object, onComplete, onError){
// this.get calls 'mayHaveChildren' and if this returns true, it will load whats needed, overwriting the 'true' into '{ item }'
this.get(object.id).then(function(fullObject){
// copy to the original object so it has the children array as well.
object.children = fullObject.children;
// now that full object, we should have an array of children
onComplete(fullObject.children);
}, function(error){
// an error occurred, log it, and indicate no children
console.error(error);
onComplete([]);
});
},
getRoot: function(onItem, onError){
// get the root object, we will do a get() and callback the result
this.get("root").then(onItem, onError);
},
getLabel: function(object){
// just get the name (note some models makes use of 'labelAttr' as opposed to simply returning the key 'name')
return object.name;
}
});
We us the variable reststore, defined above - and simply set this as the model of the tree construct parameters
var tree = new dijit.Tree({
model : treeModel
}, 'treeNode');
tree.startup();
Leaving out most of the data in our children arrays, the payload sent can be reduced and lazyloading can be taken advantage of. For every One item received (e.g. /data/parents/number1parent) a full representation of the item itself must be filled in. If he/she has children, we need to 1) name these for getting labels
in the view
- we use 'name' key, see getLabel method, 2) supply a unique ID and 3) indicate whether they may have children or not.
{
"name": "Papparazis",
"id": "root",
"children": [
{
"id": "PippoId",
"name": "Pippo",
// may have children - makes dijit.Tree create expandoNode and events
"children": true
}, {
// example leaf, children == undefined
"id": "PlutoId",
"name": "Pluto"
}, {
"id": "PaperinoId",
"name": "Paperino",
"children": true
}
]
}
With this, we will show
ROOT
+ Pippo
* Pluto
+ Paperino
Should we feel like clicking Pippo to see his child objects, lets regress; Tree requests target treeNode.item.children
which by design in the RestStore translates to a request for the object.id==PippoId - and a URL will be compiled, using the this.target/object.parent-relations/object.id
shcema. In this case, server will respond with a full representation of the ultimate URL data/PippoID
via GET.
{
"name": "Pippo",
"id": "PippoId",
"surname" : "foo",
"shoosize" : "bar",
"children": [
{
"name": "Baz",
"id": "BazId"
}, {
"name": "Godfather",
"id": "GodfatherId",
"children": true
}
]
}
Note, that the 'full representation object' - the top level - has extra entities to it, examplewise shoosize and surname. However the children are still a 'short-form representaion object'.
So, for the sake of argument, heres a representation of how one could implement the rest-capeabilities with PHP. We will go under the assumption, that papparazies is a table in sql-db and that a getPaparazi($id)
along with getPapasKids($id)
function is implemented to retrieve data columns.
First off, we need to tell apache how to handle this by a .htaccess modification. If youre new at this, check some of these resources:
Put this in /.htaccess where '/' is your DocumentRoot - and modify RewriteBase as nescessary
<IfModule mod_rewrite.c>
RewriteEngine on
# If your site is running in a VirtualDocumentRoot at http://example.com/,
# uncomment the following line:
# RewriteBase /
# If no file nor a directory exists for the request url,
# rewrite URLs of the form 'data/object' to the form 'index.php?rest=data/object'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?rest=$1 [L,QSA]
</IfModule>
From the PHP perspective in index.php (rewritten handler for all non-existing file requests) we could simply create following:
<?php
if(isset($_REQUEST['rest'])) {
// IMPORTANT; validate the request, e.g. check for referrer or similar
$ids = explode("/", $_REQUEST['rest']);
$pathCount = count($ids);
$table = array_shift($ids); // as string, 'data/' by example
if(!$table || $table == "") exit; // ... validate more
if($pathCount > 1) {
$objectRequested = array_pop($ids); // as string '' for root
}
if($pathCount > 2) {
$mid = $ids; // an array, holding rest of the path (middle relatives)
}
// with this in hand, we should be able to get the object.
// by example we're serving store data for paparazies which
// has the 'target' parameter set to 'data/'
if($table == "data") {
$fields = getPaparazi($objectRequested);
$obj = new stdobject();
$obj->name = $fields['name'];
$obj->id = $objectRequested;
$obj->shoosize = $fields['shoosize'];
$obj->children = array();
if( ( $children = getPapasKids($objectRequested) ) ) {
foreach($children as $child) {
$c_obj = new stdobject();
$c_obj->name = $child['name'];
$c_obj->id = $child['id'];
if($child['hasChildren'])
$c_obj->children = true;
$obj->children[] = $c_obj;
}
}
header("Content-Type: application/x-json; charset=XXXXX");
print json_encode($obj);
flush();
exit;
}
}
?>
See a SitePen blog for compiled info on reststores and trees
Upvotes: 14