Version 17, changed by chad 01/22/2007. Show version history
NOTE: This page is currently undergoing modification/updating. It should be finished no later than 18th November 2006
This tutorial aims to give the reader an example of using php and MySQL to populate a Dojo Tree Widget. It will also give an example of using the select event fired from the TreeSelector widget to inform the client side javascript which leaf node the user has selected.
When we're finished with the tutorial we should end up with a tree that works like this
I'm assuming you've gone through and understand the Hellowold tutorial available here http://dojo.jot.com/Tutorials/HelloWorld you will need to know how to use dojo.event.connect, dojo.io.bind and be familiar with defining widgets in html to get the most use out of this tutorial. Also, the client to server calls are done using JSON you will need the json-php library and include it in your php, see http://mike.teczno.com/json.html.
A couple words of warning, although i've tried to code as clearly as possible i am new to dojo and javascript so if any of my code could be rewritten to be more clear please let me know. Also, I use the objectId tag in a way I don't think it is ment to be. The details of why and how I use the objectId tag come up in Sending data to the server section.
you can reach me at chasd00 AT gmail DOT com
For this example i'll be making a tree listing my favorite band as the root node then their albums one level down and finally song names as leaf nodes.
First we'll setup the html and <div>'s representing the dojo widgets.
This is our basic html header with dojo.js included, we'll come back to this portion of the file and add code and "require" statements as we need them.
<html> <head> <script type="text/javascript" src="dojo.js"></script> </head> <body>
Now setup the widgets in the <body> portion of the html. From what I gather, a dojo Tree needs a TreeSelector and a TreeLoadingController widget to make it work. The TreeLoadingController is responsible for getting the data used to populate the tree and the TreeSelector is responsible for firing events as the tree is used.
TreeLoadingController
TreeSelector
Tree
TreeNode
Here's the actual markup defining the widgets in a table
<table><tr><td>Bands <div dojoType="TreeLoadingController" RPCUrl="treelisten.php" widgetId="treeController" DNDController="create"></div> <div dojoType="TreeSelector" widgetId="treeSelector"></div> <div dojoType="Tree" DNDMode="between" selector="treeSelector" widgetId="bandTree" controller="treeController"> <div dojoType="TreeNode" title="Eisley" widgetId="eisleyRoot" objectId="root" isFolder="true"></div> </td></tr></table>
Finally, add a <div> tag so that we have a place to display which leaf node was selected by the user and terminate the html
<hr> <div id="songDisplay"></div> </body></html>
At this point all of our html is done. All that is left is writing the javascript and the php backend
What we are going to do is connect the dojo.TreeSelector select event to a javascript method that figures out which node was selected by the user then outputs that node's title to the songDisplay div in the html.
First we need to include the dojo files we will need. So add the following lines to the <head> portion of the html beneath the <script type="text/javascript" src="dojo.js"></script> line
<script type="text/javascript">
dojo.require("dojo.event.*");
dojo.require("dojo.io.*");
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeLoadingController");
<!-- put code here -->
</script>
We need a javascript function that looks up the selected node and determines if it is a leaf node or not and, if it is, displays the node's title in the displaySong <div> we defined in the html.
function treeSelectFired() {
<!-- get a reference to the treeSelector and get the selected node -->
var treeSelector = dojo.widget.manager.getWidgetById('treeSelector');
var treeNode = treeSelector.selectedNode;
<!-- get a reference to the songDisplay div -->
var hostDiv = document.getElementById("songDisplay");
<!-- determine if this node is a folder (!isFolder == leaf node ) -->
<!-- get the node's title and output it to the songDisplay div -->
var isFolder = treeNode['isFolder'];
if ( !isFolder) {
var song = treeNode['title']
hostDiv.innerHTML = "You clicked on "+song;
} else {
hostDiv.innerHTHML = "";
}
hostDiv.style.display = "";
}
Now what's left is to connect the treeSelectFired function to the TreeSelector's select event. We'll do this by defining an init() function that is called by onLoad();
function init() {
<!-- get a reference to the treeSelector -->
var treeSelector = dojo.widget.manager.getWidgetById('treeSelector');
<!-- connect the select event to the function treeSelectFired() -->
dojo.event.connect(treeSelector,'select','treeSelectFired');
}
Finally, add the function init as a parameter to dojo.addOnLoad
dojo.addOnLoad(init);
Place all your javascript between the "put code here" comment and the </script> tag in the html near the "dojo.require" statements.
Now we have all our html and javascript done, the only missing piece is the php script that will feed data to the treeLoadingController based on which node is selected
Here's an example of what the tree asked for from my apache access log, if you look hard enough you can find the action, data, and dojo.preventCache parameters
127.0.0.1 - - [21/May/2006:16:08:11 -0500] "GET /treelisten.php?action=getChildren&data=%7B%22node%22%3A%7B%22widgetId %22%3A%22eisleyRoot%22%2C%22objectId%22%3A%22root%22%2C%22index%22%3A0%2C%22isFolder%22%3Atrue%7D%2C%22tree%22%3A%7B %22widgetId%22%3A%22bandTree%22%2C%22objectId%22%3A%22%22%7D%7D&dojo.preventCache=1148245691371 HTTP/1.1" 200 335
The treeLoadingController is expecting back a JSON encoded array of treeNodes which it will then display.
Before we delve into the php let me first describe the database schema i'm using to pull data from. The database contains 2 tables, album and song, that reference each other on their primary keys. I used a very simple but hopefully useful schema for this example.
schema (2 tables):
SQL example, to get all the songs from album y you would use "select song.name from song,album where song.albumId=album.id and album.name=y;"
The basic idea for treelisten.php is
1. Here's the initial php code for setting the header and extracting the parameters
<?php
// we're returning json
header('Content-type: text/json');
$action = $_REQUEST["action"];
$data = $_REQUEST["data"];
$cache = $_REQUEST["dojo.preventCache"];
// this is strange, apparently php wants to
// escape the "'s in the json data for us so we must unescape them
// for decoding to work
$data = str_replace("\\\"","\"",$data);
// instanciate a json-php instance
require_once('JSON.php');
$json = new services_JSON();
2. Now, given the action "getChildren", we use the json library to decode the data parameter and get the node object.
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
// get the node object
$node = $jsonData->node;
}
The node object has the following fields:
you access the fields in php using the "->" operator. example $title = $node->title;
Note: I needed a way to specify the parent of a given node. In this example i'm using the objectId which i'm almost certain is not how it is intended to be used. When a user clicks on a node and the call is made out to treelisten.php i need to know where in the tree the user has clicked in order to know which nodes to return. If the user clicks on the top node then objectId is set to "root" (you can see this in the html definition of TreeNode widget eisleyRoot) so i know to return the nextlevel of nodes from root, ie all the albums. In each album level node the objectId is set to "album" that way I know that a user has clicked in the album level. Again, I'm sure this is not how objectId is suppose to be used so if anyone knows the right way to do this please contact me.
At this point we have the node object the user clicked on now we need a php function that takes the node and returns an array of all the children.
3. Get the children of a given parent node
function getChildren($node) {
// setup the database connction
mysql_connect(<your connect parameters go here>);
mysql_select_db(<your db goes here>);
// get the parent node
$parent = $node->objectId;
// determine which database rows to return based on parent
if ( $parent == "root" ) {
$sql = "select * from album";
$objectId = "album" // we're returning albums
$isFolder = true; // these aren't leaf nodes
} else if ( $parent == "album" ) {
// album name is set as the widgetId
$widgetId = $node->widgetId;
$sql = "select song.name as name from song,album"
$sql .= " where albumId=album.id and album.name='$widgetId'";
$objectId = "song" // we're returning songs
$isFolder = false; // these are leaf nodes
} else { /* stub for more $parent's */ }
// assemble the resulting rows into an array of node objects.
// Each node object is represented in php by an associative array
// the array of associative arrays will then be encoded in json and
// returned back to the browser
$result = mysql_query($sql);
$i = 0;
while ( $row = msql_fetch_array($result) ) {
$node = array(
"title"=> "".$row['name'],
"widgetId" => "".$row['name'],
"objectId"=> "".$objectId,
"isFolder"=> $isFolder
);
$returnArray[$i] = $node;
$i++;
}
return $returnArray;
}
?> // nothing else to add so terminate php
ok now we have the php function that will get all of the children nodes in place lets go back to step 2. and finish the if statement Here's what we have from Step 2:
4. encode the array of children nodes to JSON
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
// get the node object
$node = $jsonData->node;
}
add the call to getChildren() and then use json_encode() and finally write the result back to the client browser
5. pass the encoded array back to the browser
if ( $action == "getChildren") {
$jsonData = $json->decode($data);
// get the node object
$node = $jsonData->node;
$nodeArray = getChildren($node);
print json_encode($nodeArray);
}
There we have it, the php script reads the parameters passed to it, decodes the data portion, figures out which nodes to return, builds an array containing the children, encodes it and finally sends it back to the client browser.
This tutorial covers a lot of ground. If you can't get it all to work then take the tutorial apart and attack it in steps. First get the widgets defined so that when you browse the html file you actually see the root node ready to be expanded. Next, click on it and watch your access logs to see the request the tree is making. You can then hardcode a single node and write it back to the tree in php (remember it has to be JSON encoded!). Keep working in steps and you will eventually get things figured out. I hope you found this tutorial helpful :)