Ajaxified Drag Drop Tree in RoR

download Ajaxified Drag Drop Tree in RoR

of 20

  • date post

  • Category


  • view

  • download


Embed Size (px)

Transcript of Ajaxified Drag Drop Tree in RoR

Ajaxified Drag Drop Tree in RoR CASE STUDY I m providing a very generalized use case where the tree fits in a good position. Here it is Consider a model Item, a controller Items. Item model is using a fabulous acts_as_tree and we are going to put a seed for Item to grow it in an ajax tree Ok no more non-code talk. So, lets start the code now ========================================================== I have also incorporated the code into a sample application which you can directly check out and try the tree yourself if you find it a headache to add the following code in a number of described files. So, here is the Sample Tree Application or you can try to code yourself as Create a sample rails application say treeapp by runningrails treeapp

from the command prompt. Now simply change your directiry into just created treeapp and make sure that you are in the directory treeapp Now configure the database settings for this application by modifying the file /config/database.yml as development: adapter: mysql database: tree_dev username: root password: root host: localhost

Here it simply shows that you have a mysql database named tree_dev and a user root with password root can access this database. So make sure about these settings. From the command prompt in application root(i.e. you are in the directory treeapp) run this command to generate the model Itemtreeapp> ruby script/generate model item

Add the following code to the file app/models/itme.rbclass Item < ActiveRecord::Base acts_as_tree validates_presence_of :name attr_accessor :style def self.roots self.find(:all, :conditions=>["parent_id = ?", 0]) end

def level self.ancestors.size end end

This simply shows that you should have a table named tems in your database so why we havnt mentioned it earlier ? Thats the thing which will make you feel an agile web development. Now look at the directory db/migrateand a you will find a file named as db/migrate/001_create_items.rb Add the following code to this file 001_create_items.rb Here we are creating our database table and also adding some initial data to work with.class CreateItems < ActiveRecord::Migration def self.up create_table "items", :force => true do |t| t.column "name", :string t.column "created_at", :datetime t.column "parent_id", :integer, :default => 0, :null => false end %w(item1 item2 item3 item4 item5).each do |name| parent = Item.new(:name=>name) parent.save Item.create(:name=>name+".1", :parent_id=>parent.id) Item.create(:name=>name+".2", :parent_id=>parent.id) Item.create(:name=>name+".3", :parent_id=>parent.id) end end def self.down drop_table :items end end

Now from the command line from the root of your application run the following command to have a table named Item in your database with some initial data.treeapp> rake db:migrate

Before we start handling our views and controller part just have a smart small image named as drag.gif in your public/images directory that we will use as a handle to drag the nodes. So, now you can see a small image at public/images/drag.gif, cool !. Now from the command line from the root of your application run the following command to create a controller treeapp> ruby script/generate controller items show

Make sure that now you have the files app/controllers/items_controller.rb and app/views/items/show.rhtml. Add the following code in the file app/controllers/items_controller.rbclass ItemsController < ApplicationController def show @items = Item.find(:all)

@item = Item.find(:first) # select according to your choice... #this item will be selected node by default in the tree when it will first be loaded. end def display_clicked_item # this action will handle the two way syncronization...all the tree nodes(items) will be linked # to this action to show the detailed item on the left of the tree when the item is clicked # from the tree if request.xhr? @item = Item.find(params[:id]) rescue nil if @item # the code below will render all your RJS code inline and # u need not to have any .rjs file, isnt this interesting render :update do |page| page.hide "selected_item" page.replace_html "selected_item", :partial=>"items/item", :object=>@item page.visual_effect 'toggle_appear', "selected_item" end else return render :nothing => true end end end def sort_ajax_tree if request.xhr? if @item = Item.find(params[:id].split("_").first) rescue nil parent_item = Item.find(params[:parent_id]) render :update do |page| @item.parent_id = parent_item.id @item.save @items=Item.find(:all) page.replace_html "ajaxtree", :partial=>"items/ajax_tree", :object=>[@item,@items] page.hide "selected_item" page.replace_html "selected_item", :partial=>"items/item", :object=>@item page.visual_effect 'toggle_appear', "selected_item" end else return render :nothing => true end end end end

Add the following code in the file app/views/items/show.rhtmlAjax Tree Application items/ajax_tree, :object=>[@item,@items] %> items/item, :object=>@item %>

Add the following code in the file app/views/items/_item.rhtml Selected Item is Item not found

Add the following code in the file app/views/items/_ajax_tree.rhtml function toggleDiv() { Element.toggle('mytree'); Element.toggle('expanded'); Element.toggle('collapsed'); return false; } function showDrag() { var drag_images = $$('img.drag_image'); drag_images.all(function(value,index){return value.style.display='inline';}); Element.toggle('done'); Element.toggle('reorder'); return false; } function hideDrag() { var drag_images = $$('img.drag_image'); drag_images.all(function(value,index){return value.style.display='none';}); Element.toggle('done'); Element.toggle('reorder'); return false; } .mytree{padding:0 0 0 0px;} .mytree li {padding:2 0 0 3px;} .outer_tree_element{margin:0 0 0 10px;} .inner_tree_element{margin:5px 0 0 10px;} .mytree a{text-decoration:none; font-size:13px; color:black;} .mytree a:hover{background-color:lightblue;} .mytree label{font-weight:normal;} .highlighted{background-color:lightblue;} .normal{background-color:white;}

.drag_image{border:0px;} {:controller=>'items', :action=>'display_clicked_item', :id=>n.id}, :loading=>"Element.show('tree_indicator')", :complete=>"Element.hide('tree_indicator')" )} %> true,:snap=>false, :handle=>"'#{node.id.to_s}_drag_image'" %> 'inner_tree_element', :url=>{:controller=>'items',:action=>'sort_ajax_tree', :parent_id=>node.id,:id=>nil}, :loading=>"Element.show('sort_tree_indicator')", :complete=>"Element.hide('sort_tree_indicator')" %> 'tree_indicator', :style=>'display:none' %> 'sort_tree_indicator', :style=>'display:none' %> var selected_el = document.getElementById('_tree_item'); selected_el.className='highlighted'; function toggleMyTree(id) { Element.toggle(id+'collapsed'); Element.toggle(id+'expanded'); Element.toggle(id+'children'); return false; } function toggleBackground(el) { // using collection proxies to change the background var highlighted_el = $$("span.highlighted"); highlighted_el.all(function(value,index){return value.className='normal'}); el.className='highlighted'; selected_el = el; return false; } function openMyTree(id)

{ Element.hide(id+'collapsed'); Element.show(id+'expanded'); Element.show(id+'children'); return false; }

As you can see in the above file we have used some indicator and toggle images. So you will be required to have three more images in the directory public/images/. Here is the small description about these images

An indicator image that will be displayed at the bottom of the tree whenever a tree node is clicked. You can select from a number of Ajax Inidicatorsavailable on the web. Select one indicator image and save in your app with the name indicator.gif. So, now make sure that you can see the image at public/images/indicator.gif Second, we need to have a small image with + sign. That will be used to toggle the tree. save it as public/images/collapsed.gif Similarly, an image with - sign. Save it as public/images/expanded.gif

We have to include the prototype and scriptaculous javascript libraries in the application. So just manually create a layout file app/views/layouts/application.rhtml and add the following code in the file application.rhtml

Now the last but the most importatntThe recursion to obtain the tree. Add the following code in the file app/helpers/application_helper.rbmodule ApplicationHelper def get_tree_data(tree, parent_id) ret = "" tree.each do |node| if node.parent_id == parent_id node.style = (@ancestors and @ancestors.include?(node.id))? 'display:inline' : 'display:none' display_expanded = (@ancestors and @ancestors.include?(node.id))? 'inline' : 'none' display_collapsed = (@ancestors and @ancestors.include?(node.id))? 'none' : 'inline' ret += "" if node.has_children? ret += " "

ret += " " end ret += " " ret += "" ret += yield node ret += "" ret += "" ret += get_tree_data(node.children, node.id){|n| yield n} ret += "" ret += "" end end ret += "" return ret end end

Now you can check the tree functionality at http://localhost:3000/items/show.. assuming that you are running your server on port 3000. njoy!! Entry Filed under: ajax, rails, tree, ajax tree, drag drop tree, navigation tree

26 Comments Add your own

1. abap | August 22nd, 2006 at 6:32 am Good

2. Ajax on Rails Ful&hellip | August 22nd, 2006 at 4:49 pm [] This tree works very fine in my application and hope it will help u also. Check out the Source Code of the tree. []

3. Source Required | September 26th, 2006 at 8:18 pm Give me the source


SUR | September 27th, 2006 at 5:37 am

Hi Source Required !! Check out the Source Code

5. Rana | November 5th, 2006 at 2:51 pm Hello Thanks for the code althou