Using YUI TreeView and DataTable Together
October 12, 2009 at 5:33 pm by Carlos Bueno | In Development | 4 CommentsWhen you want to present both a complex hierarchy and lists of properties, the TreeView and DataTable Controls in YUI 2 work well together.
For this tip we will make a browser for web server logs. The TreeView will display file and folder paths, and the DataTable will display individual log lines. Clicking on a file or folder in the Tree will cause the DataTable to filter out all but that path. (Click here for the working demo.)
For brevity’s sake I will use static data. In practice you would use the dynamic data techniques detailed here and here and do the filtering on the server. After the static example, we’ll talk about dynamic data in more detail.
The bit to pay attention to is the linkage between the TreeView’s labelClicked event and the DataSource’s sendRequest() method. (You can find all the dependencies for this example on the YUI 2 Configurator.)
<!-- Combo-handled YUI CSS files: -->
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/combo?2.8.0r4/build/fonts/fonts-min.css&2.8.0r4/build/base/base-min.css&2.8.0r4/build/datatable/assets/skins/sam/datatable.css&2.8.0r4/build/treeview/assets/skins/sam/treeview.css">
<!-- Combo-handled YUI JS files: -->
<script type="text/javascript" src="http://yui.yahooapis.com/combo?2.8.0r4/build/yahoo-dom-event/yahoo-dom-event.js&2.8.0r4/build/element/element-min.js&2.8.0r4/build/datasource/datasource-min.js&2.8.0r4/build/datatable/datatable-min.js&2.8.0r4/build/treeview/treeview-min.js"></script>
<!-- create container DIVs for the TreeView and DataTable -->
<body class="yui-skin-sam">
<table>
<tr>
<td valign="top">
<div id="myTree" style="width:160px"></div>
</td>
<td valign="top">
<div id="myContainer"></div>
</td>
</tr>
</table>
</body>
<script>
YAHOO.util.Event.addListener(window, "load",
function() {
/**** DataTable ****/
// Define the columns of the DataTable
var myColumnDefs = [
{key:"ip", label:"IP Address"},
{key:"path", label:"Path"},
{key:"status", label:"Status"},
{key:"msec", label:"Time"},
{key:"bytes", label:"Size"},
{key:"ts", label:"Timestamp"}
];
// Dummy data for the table.
var myData = [
{ts:'Oct 10 2009 14:33:26', ip:'1.2.3.4', path:'/favicon.ico', status:200, msec:123, bytes:616},
{ts:'Oct 10 2009 14:33:26', ip:'1.2.3.4', path:'/images/logo.gif', status:200, msec:213, bytes:7891},
{ts:'Oct 10 2009 14:33:26', ip:'1.2.3.4', path:'/images/welcome.gif', status:200, msec:872, bytes:19357},
{ts:'Oct 10 2009 14:33:26', ip:'1.2.3.4', path:'/index.html', status:200, msec:901, bytes:13453},
{ts:'Oct 10 2009 14:33:27', ip:'4.5.6.7', path:'/favicon.ico', status:304, msec:110, bytes:616},
{ts:'Oct 10 2009 14:33:27', ip:'4.5.6.7', path:'/images/logo.gif', status:304, msec:432, bytes:7891},
{ts:'Oct 10 2009 14:33:27', ip:'4.5.6.7', path:'/images/welcome.gif', status:304, msec:528, bytes:19357},
{ts:'Oct 10 2009 14:33:27', ip:'4.5.6.7', path:'/index.html', status:304, msec:562, bytes:13453},
{ts:'Oct 10 2009 14:33:28', ip:'3.4.5.6', path:'/favicon.ico', status:200, msec:313, bytes:616},
{ts:'Oct 10 2009 14:33:28', ip:'3.4.5.6', path:'/images/logo.gif', status:200, msec:215, bytes:7891},
{ts:'Oct 10 2009 14:33:28', ip:'3.4.5.6', path:'/images/welcome.gif', status:200, msec:324, bytes:19357},
{ts:'Oct 10 2009 14:33:28', ip:'3.4.5.6', path:'/index.html', status:200, msec:818, bytes:13453},
{ts:'Oct 10 2009 14:33:29', ip:'7.8.9.5', path:'/favicon.ico', status:200, msec:786, bytes:616},
{ts:'Oct 10 2009 14:33:29', ip:'7.8.9.5', path:'/images/logo.gif', status:200, msec:604, bytes:7891},
{ts:'Oct 10 2009 14:33:29', ip:'7.8.9.5', path:'/images/welcome.gif', status:200, msec:803, bytes:19357},
{ts:'Oct 10 2009 14:33:29', ip:'7.8.9.5', path:'/index.html', status:200, msec:934, bytes:13453}
];
// Create a static "JSARRAY" DataSource with the appropriate fields.
var myDataSource = new YAHOO.util.DataSource(myData);
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
myDataSource.responseSchema = {
fields: ['ip', 'path', 'status', 'msec', 'bytes', 'ts']
};
// Create a filter function that will be called any time sendRequest() is called.
// Returns only the results whose path matches the given prefix.
myDataSource.doBeforeCallback = function (path, raw, res) {
var data = res.results || [];
var filtered = [];
if (path) {
for (var i=0; i<data.length; i++) {
if (data[i].path.indexOf(path) === 0) {
filtered.push(data[i]);
}
}
res.results = filtered;
}
return res;
};
// Create the DataTable instance given the HTML element, column defs and datasource.
var myDataTable = new YAHOO.widget.DataTable("myContainer", myColumnDefs, myDataSource);
/**** TreeView ****/
// Dummy tree data, describing a folder and some files.
var myTreeData = [
{name:'images', children:[
{name:'logo.gif'},
{name:'welcome.gif'}
]},
{name:'index.html'},
{name:'favicon.ico'}
];
var myTree = new YAHOO.widget.TreeView("myTree");
// Recurse over the tree data to build nodes.
// Expand the second level of files & folders, but keep the rest hidden.
function buildNodes(parentNode, treeData, parentPath, expanded) {
for (var i=0; i<treeData.length; i++) {
var nodeData = treeData[i];
var node = new YAHOO.widget.TextNode(nodeData.name, parentNode, expanded);
// Build up the full path of the node for future reference.
node.path = parentPath + '/' + node.label;
// Recurse downward if this node has children.
if (nodeData.children) {
buildNodes(node, nodeData.children, node.path, false);
}
}
}
buildNodes(myTree.getRoot(), myTreeData, '', true);
// When a tree node is clicked, filter the DataTable's records
// by poking at the DataSource's sendRequest() method.
myTree.subscribe("labelClick", function(node) {
myDataSource.sendRequest(node.path,{
success : myDataTable.onDataReturnInitializeTable,
failure : myDataTable.onDataReturnInitializeTable,
scope : myDataTable,
argument: myDataTable.getState()
});
});
myTree.draw();
});
</script>
Using TreeView and DataTable with Dynamic Data
To use dynamic data, server-side pagination and sorting with a DataTable, check out this example. To use dynamic data in a TreeView, refer to this example. In the loadNodeData function you can add the call to myDataSource.sendRequest() like so:
// construct a server-side call that will return logs matching the given path
var nodePath = encodeURIComponent(node.path);
var requestString = 'path=' + nodePath + '&sort=ts&dir=asc&startIndex=0&results=25';
// poke at the DataSource using sendRequest()
var oCallback = {
success : myDataTable.onDataReturnSetRows,
failure : myDataTable.onDataReturnSetRows,
scope : myDataTable,
argument: myDataTable.getState()
};
myDataSource.sendRequest(requestString, oCallback);
Share and extend: Bookmark with del.icio.us | digg it! | reddit!
4 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment

Copyright © 2006-2010 Yahoo! Inc. All rights reserved. Privacy Policy - Terms of Service
Powered by WordPress on Yahoo! Web Hosting.


This is something that we rely on quite a bit in our product, and I believe it is just the most awesome display of YUI’s awesomeness. We add context menus to both the tree and the table for extra fun functionality. We have customers with systems that have millions of records to search/browse/manage, and YUI has helped us make it usable. Awesome.
Comment by Brian Holub — October 12, 2009 #
So, let’s say I’m using asp.net mvc and I push json data down to the view.
What is the best way then to :
var myDataSource = new YAHOO.util.DataSource(myData);
I don’t think putting server side tags is possible in javascript right?
ie.
var myDataSource = new YAHOO.util.DataSource();
Let’s assume here Model.ToJson provides exactly what your ‘myData’ does.
If I could get through understanding ‘melding’ this data from the server to the javascript it would go a long way in understanding how to use these ‘controls’.
(email me if you want)
Great post – thanks!
Comment by Steve — October 26, 2009 #
it took my server tags away in that post :)
I was trying to show passing ViewData.Model using server side tags to the DataSource call
Comment by Steve — October 26, 2009 #
Is there a way to prevent the filtering from generating another request? I simply want to grab a pile of data from another page in the initial request to build the table and tree, and then have clicking on the tree alter the table.
Comment by Joe — March 17, 2010 #