Recently I been playing around with alot of Ext JS.  It’s a great Ajax Framework, which makes building a full Ajax application easy as pie.  All you really need to worry about is how to transfer Json between your Rails application and Ext JS.

Here’s a tutorial on how to get Ext JS’s TreePanel going with Ruby on Rails.

Ext JS Implementation

You’ll start by creating the Tree component in your view. This is an example of how it should look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var tree = new Ext.tree.TreePanel({
      useArrows:true,
      autoScroll:true,
      animate:true,
      containerScroll: true,
      // auto create TreeLoader
      loader: new Ext.tree.TreeLoader({dataUrl:'/groups.json', requestMethod: 'GET'}),
 
      root:  new Ext.tree.AsyncTreeNode({
        text: 'Root Node',
        draggable:false,
        disabled: true,
        expandable: false,
        expanded: true,
        id:'source'  
      })
});

The most important line in this code above is line number 7. This will call the URL to retrieve the Tree in Json format. I use a GET request so it will goto my index action in Rails using a Restful design.

Rails Tree Plugin

As you may have noticed from my example I’m using Group as the model for my tree. In your model you should install a tree plugin similar to acts_as_tree so you get access to methods such as group.children.

If your using the above plugin, add this line into your model to specify your model to use the plugin:

# /app/models/group.rb
class Group < ActiveRecord::Base
  acts_as_tree :order => "name", :dependent => :destroy
  ....
end

If you find acts_as_tree to be limited in functionality, there are better tree plugins that I recommend having a look at such as acts_as_nested_set

Rails Model Implementation

For my implementation I needed a boolean function to return true/false if group was a leaf node or not. I added this function into my model.

# /app/models/group.rb
  ...
  def leaf?
    if(children.size == 0)
      false
    else
      true
    end
  end
  ...

Also I added a function to return the root node groups.

  # /app/models/group.rb
  ...
  def self.find_root_nodes
    find(:all, :conditions => ["parent_id IS NULL"])
  end
  ...

Handling JSON requests with Rails

Using a Restful design here. I made my controller return json using .json at the end of the URL. To allow this I added this line to the end of my routes.rb file

# END OF /config/routes.rb
  ...
  map.connect ':controller/:action.:format'
  ...

Rails Controller Implementation

My index action in my controller serves the GET request and spits out json when .json is supplied at the end of the URL. A extra function (get_ext_tree) is required to recursively add the children to a tree node. This function will return JSON in the format required for the Ext JS TreeLoader.

# /app/controllers/groups_controller.rb
class GroupsController < ApplicationController
  def index
      groups = Group.find_root_nodes
      json_data = get_ext_tree(groups,nil)
    end
 
    respond_to do |format|
      format.html  #/groups
      format.json { render :json => json_data } # /groups.json
    end
  end
 
  def get_ext_tree(groups, parent)  
    data = Array.new  
    groups.each { |group|  
      if group.leaf?  
        if data.empty?  
          data =   [{"text"  =>  group.name, "id"  => group.id, "leaf"  => false,  
              "children" => get_ext_tree(group.children,group) }]   
        else  
          data.concat([{"text"  =>  group.name, "id"  => group.id, "leaf"  => false,  
                "children" => get_ext_tree(group.children,group)}])  
        end  
      else  
        data.concat([{"text" => group.name, "id" => group.id, "leaf" => true, "children" => []}])   
      end  
    }  
    return data  
  end
 
end