HOW-TO: Integrate a YUI tree with Rails

July 16th, 2006

In this episode of “Getting the Yahoo Interface Library to play with Rails” we will focus on trees. If you haven’t gone through it already, I suggest you at least give the first article in the series a glance. That article will give you a quick overview of how to dowload the YUI and configure a Rails project to make use of it. In this article, we will build a simple tree and, in the process, build a helper class that you can use in your projects to simplify the view code. Another useful reference is Yahoo’s reference page for the tree API.

When you are done with this tutorial, you will build a simple tree that you can invoke with: http://0.0.0.0:3000/tree/display

Configure your Rails Project for Yahoo Trees

Start by creating your YuiExample Rails project. Once that is created (or if you have already created it from the last example), create a controller called “tree”.

As before, you will need to copy some files from the yahoo directory structure into a Rails friendly one (NOTE: These instructions assume you have named your project YuiSample).

  1. Copy treeview.js: Copy treeview.js from /yui/build/treeview to /YuiSample/public/javascript/yui
  2. Copy trees.css: Copy trees.js from /yui/build/treeview/assets to /YuiSample/public/stylesheets/yui
  3. Copy tree imgates: Copy all the image files from /yui/build/treeview/assets to /YuiSample/public/images/yui/tree

At this point you have all the YUI files you need, but trees.css containts the wrong image URLs. Replace trees.css with this one:


/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */

/* first or middle sibling, no children */
.ygtvtn {
	width:16px; height:22px; 
	background: url(/images/yui/tree/tn.gif) 0 0 no-repeat; 
}

/* first or middle sibling, collapsable */
.ygtvtm {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/tm.gif) 0 0 no-repeat; 
}

/* first or middle sibling, collapsable, hover */
.ygtvtmh {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/tmh.gif) 0 0 no-repeat; 
}

/* first or middle sibling, expandable */
.ygtvtp {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/tp.gif) 0 0 no-repeat; 
}

/* first or middle sibling, expandable, hover */
.ygtvtph {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/tph.gif) 0 0 no-repeat; 
}

/* last sibling, no children */
.ygtvln {
	width:16px; height:22px; 
	background: url(/images/yui/tree/ln.gif) 0 0 no-repeat; 
}

/* Last sibling, collapsable */
.ygtvlm {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/lm.gif) 0 0 no-repeat; 
}

/* Last sibling, collapsable, hover */
.ygtvlmh {
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/lmh.gif) 0 0 no-repeat; 
}

/* Last sibling, expandable */
.ygtvlp { 
	width:16px; height:22px; 
	cursor:pointer ;
	background: url(/images/yui/tree/lp.gif) 0 0 no-repeat; 
}

/* Last sibling, expandable, hover */
.ygtvlph { 
	width:16px; height:22px; cursor:pointer ;
	background: url(/images/yui/tree/lph.gif) 0 0 no-repeat; 
}

/* Loading icon */
.ygtvloading { 
	width:16px; height:22px; 
	background: url(/images/yui/tree/loading.gif) 0 0 no-repeat; 
}

/* the style for the empty cells that are used for rendering the depth 
 * of the node */
.ygtvdepthcell { 
	width:16px; height:22px; 
	background: url(/images/yui/tree/vline.gif) 0 0 no-repeat; 
}

.ygtvblankdepthcell { width:16px; height:22px; }

/* the style of the div around each node */
.ygtvitem { }  

/* the style of the div around each node's collection of children */
.ygtvchildren { }  
* html .ygtvchildren { height:2%; }  

/* the style of the text label in ygTextNode */
.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { 
	margin-left:2px;
	text-decoration: none;
}

.ygtvspacer { height: 10px; width: 10px; margin: 2px; }

Examing a simple YUI tree

The first thing to try is creating a simple hard-coded tree. Create a new RHTM file in YuiSample/app/views/tree called display.rhtml and paste the following code in it:




  YUI Tester: <%= controller.action_name %>
  <%= javascript_include_tag "yui/yahoo",  
                             "yui/dom",
                             "yui/event",
                             "yui/connection",
                             "yui/dragdrop",
                             "yui/treeview"%>
    <%= stylesheet_link_tag  'yui/tree'%>

    <%= stylesheet_link_tag 'appstylesheet'%>
 
 
 

    

There are three sections of this RHTML file.

  1. The header section simply imports the YUI javascript and stylesheets we need.
  2. The next section is really just one line:

    . This section defines the div object that the YUI API will use to build the tree object.

  3. The last section is in the CDATA block we define. That is the javascript that defines and draws the tree. I don’t know if this is a hard requirement, but unlike the dialog objects, I had to define the javascript AFTER I defined the div tag in order for the functions to work.

In this particular example I am making use of the href property so each tree node is a clickable reference. By default, clicking on the node expands and collapses it. Refer to the Yahoo User Interface library link I provided above to see the various options for the TextNode constructor.

Designing a Rails friendly tree rendering algorithm

So the prior exampe was useful for understanding how the YUI API builds tree, but it is not really all that great for dynamic content generation using RoR. To solve this, I quickly put together an algorithm whose javascript and supporting datastructures will be easy to generate. This is done by first creating a datastructure that represents the pre-order traversal of the tree and then passing that datastructure into a general purpose javascript function that generates the tree using the YUI API.

The datastructure will be of the following form:


    tree_data = [node_level_num, {yui tree node object def},...]

So, for example:


var test_data = 
	            [0, { label: "Cat 1", href:"http://www.yahoo.com" }, 
	             1, { label: "Cat 1.1", href:"http://www.yahoo.com" },
	             1, { label: "Cat 1.2", href:"http://www.yahoo.com" },
	             2, { label: "Cat 1.2.1", href:"http://www.yahoo.com" },
	             2, { label: "Cat 1.2.2", href:"http://www.yahoo.com" },
	             1, { label: "Cat 1.3", href:"http://www.yahoo.com" },
	             0, { label: "Cat 2", href:"http://www.yahoo.com" }];

In the above example, the datastructure shows that:

  • Categores 1 and 2 are level 0 in the tree, Categores 1.1, 1.2, and 1.3 are level 1 in the tree, and Categories 1.2.1, 1.2.2 are level 2 in the tree.
  • Since this is a pre-order listing of the tree, it shows that Category 1 is the parent of Categories 1.1, 1.2, and 1.3
  • …and that Category 1.2 is the parent of categores 1.2.1 and 1.2.2.

Now all we need is some code that takes this datastructure as input and generates the YUI tree:


function addChildrenNodes(currLevel, nodeIndex, parent) { 
  var lastNode;
  var level = currLevel;

  while (nodeIndex < test_data.length) {
    var level = test_data[nodeIndex];
    if (level == currLevel) {
       nodeIndex++;
       lastNode = new YAHOO.widget.TextNode(test_data[nodeIndex++], parent, false);
    } else if (level < currLevel) {
       return nodeIndex;
    } else {
       nodeIndex = addChildrenNodes(level, nodeIndex, lastNode);
    }
  }
  return nodeIndex;
}

function mytree_Init() {
  var tree = new YAHOO.widget.TreeView("mytree");
  addChildrenNodes(0, 0, tree.getRoot());
  tree.draw();
}

YAHOO.util.Event.addListener(window, "load", mytree_Init);

The workhorse function is addChildrenNodes. It is a recursive algorithm that iterates as long as there is data available and for each iteration, checks the level of the next set of data and does the following:

  • The next node is at the current level: If the next node is at the current level, grab the level and the next node and advance the index through the node data. Create a YUI tree node and add it to the parent.
  • The next node is at a higer level num: This means the next node is lower in the tree and is a child of the last element added. Recursively call addChildrenNodes using the new level number and passing in the last element added as the parent. Advance the nodeIndex to whatever AddChildrenNodes tells us to.
  • The next node is at a lower level num: This means we are done with this branch -- so return the current nodeIndex.
  • We run out of data: in this case we are done with our tree, return the current nodeIndex.

Wrapping things up in a helper

This is a lot of code to remember and I always say that a line of code is a bug waiting to happen. Plus we don't want to violate DRY (don't repeat yourself), so create the following helper in TreeHelper:


module TreeHelper
  def yui_build_tree(tree_id, tree_data)
  
  return "//

Once we have this helper, we can now replace display.rhtml with:




  YUI Tester: <%= controller.action_name %>
  <%= javascript_include_tag "yui/yahoo",  
                             "yui/dom",
                             "yui/event",
                             "yui/connection",
                             "yui/dragdrop",
                             "yui/treeview"%>
    <%= stylesheet_link_tag  'yui/tree'%>

    <%= stylesheet_link_tag 'appstylesheet'%>
 
 
 

    

Next Steps

As you can see from this example, I am still hardcoding the data. The next thing to do would be to hook this up to a model object and create a helper that generates the tree data structure. I recommend leveraging the methods from "act_as_tree" in the helper. The other thing that would be nice would be adding drag and drop to re-arrange the tree structure. Hmm.... guess I better get back to it to figure out how to do that :)

Entry Filed under: How-to,Ruby,YUI

Trackbacks

9 Comments Add your own

  • 1. LeonardoBighi.com - Progr&hellip  |  June 16th, 2008 at 8:10 pm

    links from Technoratifornece todo o mecanismo necessário para a criação deste tipo de lista, e o Rails nos fornece os meios práticos e rápidos de gerar dinamicamente estas listas. Todo o procedimento para usar o treeview da YUI em conjunto com o Rails pode ser vistoneste endereço

  • 2. HowToWorkWithTheYUI in Ru&hellip  |  July 16th, 2006 at 7:25 pm

    Kramer auto Pingback[…] HOW-TO: Integrate a YUI tree with Rails […]

  • 3. RoR Wiki Wiki - HowT&hellip  |  July 20th, 2006 at 9:50 am

    Kramer auto Pingback[…] HOW-TO: YUI ĥ꡼ Rails Last modified:2006/07/20 17:41:05 Keyword(s):[Howto] References:[RailsWikiIndex] […]

  • 4. Sonjaya Tandon » HO&hellip  |  August 9th, 2006 at 12:18 am

    […] Before you begin with this tutorial, make sure you have gone through HOW-TO: Integrate a YUI tree with Rails. This tutorial picks up where that one left off. […]

  • 5. HowToWorkWithTheYUI (Vers&hellip  |  August 24th, 2006 at 3:04 am

    Kramer auto Pingback[…] HOW-TO: Integrate a YUI tree with Rails […]

  • 6. sheynkman  |  December 19th, 2006 at 4:58 pm

    If you want to see a site built completely with Ruby/Rails/YUI and YUI-ext, check out the one I built: HouseMath 2.0. I still have prototype in there for rails’ sake, but other than that, it’s YUI with lots of rails helpers.

  • 7. sonjaya  |  December 20th, 2006 at 9:32 am

    I checked out your site sheynkman. Looks like you put a good amount of work into that. Site looks great, btw.

  • 8. luan nguyen  |  February 24th, 2007 at 7:19 pm

    I have a banner, sidebar, and main div(s).

    I am using yui tree view on my sidebar. When user click on my sidebar, it goes to appropriate controller.

    The problem that I have is that my yui tree view sidebar collapse whenever I click on the sidebar. It seems like the whole page get refreshed.

    What would I do so that the sidebar doesn’t get refreshed when I click on it.

    Thanks a lot.
    Luan

    Here is my views/layouts/application.rhtml

    Test
    “all”%>

    // ‘test1’, :action => ‘list’) %>” },
    1, { label: “Cat1.2″, href:” ‘test2’, :action => ‘list’) %>” },
    0, { label: “Cat 2”},
    1, { label: “Cat 2.1″, href:” ‘test3’, :action => ‘list’) %>” },
    1, { label: “Cat 2.2″, href:” ‘test4’, :action => ‘list’) %>” }];
    //]]>

  • 9. StumbleUpon » Your p&hellip  |  April 11th, 2007 at 3:37 am

    Kramer auto Pingback[…] Your page is on StumbleUpon […]

  • 10. links for 2007-04-27 &laq&hellip  |  April 27th, 2007 at 4:32 pm

    […] Sonjaya Tandon » HOW-TO: Integrate a YUI tree with Rails (tags: rubyonrails YUI tree javascript) […]

Leave a Comment

You must be logged in to post a comment.

Trackback this post  |  Subscribe to the comments via RSS Feed


Calendar

July 2006
M T W T F S S
« Jun   Aug »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Most Recent Posts