Ajaxified Drag Drop Tree in RoR
原文: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/
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
class Item < ActiveRecord::Basevalidates_presence_of :nameattr_accessor :styledef self.roots self.find(:all, :conditions=>["parent_id = ?", 0])enddef level self.ancestors.sizeendend
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.
class CreateMyItem < ActiveRecord::Migrationdef 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) endend def self.down drop_table :items endend
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
class ItemController < ApplicationControllerdef 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.enddef 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 endenddef 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 endend
Add the following code in the file app/views/items/show.rhtml
<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>
Add the following code in the file app/views/items/_item.rhtml
<% if @item %><h2>Selected Item is <%=h @myitem.name%> <h2><% else %>Item not found<% end %>
Add the following code in the file app/views/items/_ajax_tree.rhtml
<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 backgroundvar 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>
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
module ApplicationHelperdef 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' onclick='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' onclick='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 endend
Now you can check the tree functionality at http://localhost:3000/items/show.. assuming that you are running your server on port 3000. njoy!!
页:
[1]