Ajaxified Drag Drop Tree in RoR

原文:
[url]http://vinsol.com/2006/08/18/source-code-for-ajax-based-drag-drop-navigation-tree-in-ruby-on-rails-the-tree-works-well-with-firefox-and-ie-6/[/url]

From the command prompt run this command to generate the model Item…

$ ruby script/generate model item
Add the following code to the file app/models/item.rb

[code]class Item < ActiveRecord::Base

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[/code]
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/00x_create_item.rb where 00x represents the next version of your schema info.
Add the following code to this file 00x_create_item.rb
Here we are creating our database table and also adding some initial data to work with.

[code]class CreateMyItem < 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[/code]
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.

$ 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 …

$ 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.rb

[code]class ItemController < 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(param[: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)
@sort = 'inline'
page.replace_html "ajaxtree", :partial=>"items/ajax_tree",
:object=>[@item,@items,@sort]
end
end
end
end

end[/code]
Add the following code in the file app/views/items/show.rhtml

[code]<h2>Ajax Tree Application</h2>

<div id=”ajaxtree” style=”width:40%;float:left;”>
<%= render :partial=>’items/ajaxtree’, :object=>[@item,@items] %>
</div>

<div id=”selected_item”>
<%= render :partial=>’items/item’, :object=>@item %>
</div>[/code]
Add the following code in the file app/views/items/_item.rhtml

[code]<% if @item %>
<h2>Selected Item is <%=h @myitem.name%> <h2>
<% else %>
Item not found
<% end %>[/code]
Add the following code in the file app/views/items/_ajax_tree.rhtml


[code]<script type="text/javascript">

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;
}
</script>

<style>
.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:2px 0 0 8px;
}

.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;
}
</style>

<div id="mytree" class="mytree">

<% @ancestors = @item.ancestors.collect{|parent| parent.id} unless !@item.has_parent? %>
<% @items = Item.find(:all) %>
<%= get_tree_data(@items,0){|n|
link_to_remote n.name,
:url=>{:controller=>'items',:action=>'display_clicked_item',:id=>n.id},
:loading=>"Element.show('tree_indicator')",
:complete=>"Element.hide('tree_indicator')"
} %>
<% @items.each do |node| %>

<%= draggable_element node.id.to_s+'_tree_div',:revert=>true,:snap=>false, :handle=>"'#{node.id.to_s}_drag_image'" %>
<%= drop_receiving_element(node.id.to_s+'_tree_div',:accept=>'inner_tree_element',
:url=>{:controller=>'collection',:action=>'sort_my_tree',:parent_id=>node.id,:id=>nil},
:loading=>"Element.show('sort_tree_indicator')",
:complete=>"Element.hide('sort_tree_indicator');"
)%>

<% end %>

<%= image_tag 'indicator.gif', :id=>'sort_tree_indicator', :style=>'display:none' %>

</div>

<script type="text/javascript">

var selected_el = document.getElementById('<%=@item.id%>_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;
}

</script>[/code]
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
Now the last but the most importatnt…The recursion to obtain the tree.
Add the following code in the file app/helpers/application_helper.rb

[code]module ApplicationHelper

def get_tree_data(tree, parent_id)
ret = "<div class='outer_tree_element' >"
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 += "<div class='inner_tree_element' id='#{node.id}_tree_div'>"
if node.has_children?
ret += "<img id='#{node.id.to_s}expanded' src='/images/expanded.gif' οnclick='javascript: return toggleMyTree(\"#{node.id}\"); ' style='display:#{display_expanded}; cursor:pointer;' /> "
ret += "<img style='display:#{display_collapsed}; cursor:pointer;' id='#{node.id.to_s}collapsed' src='/images/collapsed.gif' οnclick='javascript: return toggleMyTree(\"#{node.id.to_s}\"); ' /> "
end

ret += " <img src='/images/drag.gif' style='cursor:move' id='#{node.id}_drag_image' align='absmiddle' class='drag_image' /> "

ret += "<span id='#{node.id}_tree_item'>"
ret += yield node
ret += "</span>"
ret += "<span id='#{node.id}children' style='#{node.style}' >"
ret += get_tree_data(node.children, node.id){|n| yield n}
ret += "</span>"
ret += "</div>"
end
end
end[/code]
Now you can check the tree functionality at http://localhost:3000/items/show.. assuming that you are running your server on port 3000. njoy!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值