#147
Feb 02, 2009

Sortable Lists

Creating drag & drop sortable lists is fairly easy using Prototype and the built in helper methods. See how in this episode.
Download (15.4 MB, 8:14)
alternative download for iPod & Apple TV (10.8 MB, 8:14)

Resources

script/generate migration add_position_to_faqs position:integer
rake db:migrate
script/plugin install git://github.com/rails/acts_as_list.git
# routes.rb
map.resources :faqs, :collection => { :sort => :post }

# faqs_controller.rb
def sort
  params[:faqs].each_with_index do |id, index|
    Faq.update_all(['position=?', index+1], ['id=?', id])
  end
  render :nothing => true
end

# models/faq.rb
class Faq < ActiveRecord::Base
  acts_as_list
end
<!-- layouts/application.html.erb -->
<%= javascript_include_tag :defaults %>

<!-- faqs/index.html.erb -->
<ul id="faqs">
<% for faq in @faqs %>
  <% content_tag_for :li, faq do %>
    <span class="handle">[drag]</span>
    <%= link_to h(faq.question), faq %>
  <% end %>
<% end %>
</ul>
<%= sortable_element("faqs", :url => sort_faqs_path, :handle => "handle") %>
li .handle {
  font-size: 12px;
  cursor: move;
  color: #777;
}

RSS Feed for Episode Comments 39 comments

1. Tilo S. Feb 02, 2009 at 01:02

Excellent RailsCast!
thank you! :)


2. Andy L. Feb 02, 2009 at 01:22

How would you go about doing something similar with jQuery?

I mean, pure jQuery, without the jRails plugin...


3. Radovan Feb 02, 2009 at 01:40

Andy,

Try this:

http://www.wil-linssen.com/extending-the-jquery-sortable-with-ajax-mysql/


4. Mario U. Feb 02, 2009 at 01:42

What's the difference between content_tag and content_tag_for? I'm always using content_tag in my rails projects.


5. Alain Ravet Feb 02, 2009 at 01:54

Don't forget to mention that the SQL indexes on the 'position' column MUST NOT be unique, because of the temporary duplicates created by the progressive updates in the sort action.


6. Lennart Feb 02, 2009 at 02:27

Wow. This just describes so good why I love Rails! :)


7. tony petruzzi Feb 02, 2009 at 05:16

an idea for next weeks rails cast. expand upon this using categories. in an application i have now, i have my faqs linked to a specific category. my users can order the categories themselves in addition to the group of faqs under each category.


8. Andrew Feb 02, 2009 at 06:13

How hard would it be to extend this to nested lists, using acts_as_tree? In particular, dragging a sublist item to another sublist, or making it a parent (out-denting)?


9. pulkit Feb 02, 2009 at 07:03

Hi Ryan,
I am happy with your railscast video but i can't download source code.
When I tried to download source code of all episodes i got source code upto episode no.136 only.
Please check it out of there is any prob. in git.

Cheers,
:)


10. Chris J Feb 02, 2009 at 09:52

If any of you out there are trying to use Ryan's technique to sort <tr> elements inside a table, make sure you

(1) read the documentation for Sortable.create, especially the important information under "Notes" (see http://wiki.github.com/madrobby/scriptaculous/sortable-create), and
(2) pass :tag => "tr" to your call to the sortable_element helper method.


11. StartBreakingFree.com Feb 02, 2009 at 10:25

Ironically...the most valuable thing I got out of this was "each_by_index", haha! Found myself using a separate indexer "i" a number of times, had no idea this existed! Thanks Ryan.


12. Henrik N Feb 02, 2009 at 10:35

This is what I do with jQuery: http://henrik.nyh.se/2008/11/rails-jquery-sortables

It also uses a single query to update the index, which could be used just as well with Prototype.


13. Scott Hunter Feb 02, 2009 at 14:31

Thanks for the example! I needed to do something like this today, and while I could have put it together myself from the pieces, your example is a complete picture of how I wanted to implement it, even down to the collection RESTful action :) And to think I stumbled across it through pure luck from Google, even before iTunes had downloaded today's video from the podcast.


14. Ubercow Feb 02, 2009 at 20:47

I was just trying to do this today. Thanks a bunch!


15. Jcooper Feb 03, 2009 at 01:11

Awesome as usual... Just wondering is this the same for organizing a gallery of images? If they are not just a list but in rows and columns?


16. Kip Feb 03, 2009 at 10:15

Thanks for the episode. I've learned a ton from you and am very thankful. If I can vote for a future episode topic, I'd like to see something about using an asset service like Amazon S3 with Rails. I have some upcoming projects that want to do heavy video streaming and have been thinking about using something like S3 for the application. Thanks!


17. Niklas Feb 03, 2009 at 12:10

Very nice tutorial, as always. Very useful and on point. Thank you.


18. Joe Ellis Feb 03, 2009 at 17:21

I JUST finished figuring out this exact same thing, and then a railscasts comes along! Crazy


19. Gustavo Scanferla Feb 03, 2009 at 18:03

Thanks, that's what I needed!

But in my app, each user will have the power to sort the list in his own way.

I'm trying a "rich join table" with (user_id, item_id, position) to relate a item to a user and to know it's position in relation to this specific user.

But I keep getting errors. I know this insn't very clear... But if someone can help me how to do something like that, I would be thankful!


20. Gustavo Scanferla Feb 03, 2009 at 18:15

Continuing:
To make it easier, I am logging successfully the data that I want:
 
item_id: 18, position: 1 and user_id: 1
item_id: 38, position: 2 and user_id: 1
item_id: 29, position: 3 and user_id: 1
item_id: 25, position: 4 and user_id: 1
item_id: 33, position: 5 and user_id: 1

I just need to save/update this in my database.

Ryan, I apologize about the semi-offtopic, but I don't know, maybe someone wants to do a personalized sortable lists too =D


21. Gustavo Scanferla Feb 03, 2009 at 19:52

Yeah! I made it!

Maybe it's not the best way to do that, but it's working.

If someone wants to know how I did it just reply here. For now I'll say sorry about the "flood" and thanks!

PS: Ryan, feel free to delete my 3 comments =)


22. R Sturim Feb 04, 2009 at 10:59

Tutorials like these, the ones that focus on the "basics" are crucial to making our community standout from other web frameworks.

All average rails devs should be able to whip off a sortable draggable list in minutes. This screencast makes a nice dent in the right direction. I know there is voting out on UserVoice, but I would strongly encourage you to consider extending a few additional ajax centric screencasts similar to this to move our cumulative ball further down the field.

Perhaps centering on a few of the other scriptaculous modules such as expandable-collapsible divs, inline editing, ajax tabs, sliders, etc.

Essentially a toolbox of the common UI patterns we are commonly asked to provide our clients but are not always entirely obvious to implement.

Kudos again,

-Rich


23. Shreyans Feb 05, 2009 at 12:56

I am not sure how this will add position column to the faqs table.

script/generate migration add_position_to_faqs position:integer

Any idea on what's going on here?


24. Pete Feb 06, 2009 at 02:01

Thank you for the download.


25. Michael Feb 06, 2009 at 07:02

Ryan, awesome screencast.
R Sturim, I Totally agree. We definitely need more screencasts like this.


26. Achim Feb 07, 2009 at 01:36

Great work again Ryan .. keep on casting :-)


27. Ryan Bates Feb 07, 2009 at 08:50

@Mario, content_tag_for accepts a model as an argument, and it will give the tag attributes based on the model's name and id.

@pulkit, weird. Do you still have this problem? Perhaps GitHub was having difficulties.

@Jcooper, great question! If you just float the list elements to the left it should work. You'll also want to set the
":constraint => false" option on sortable_element so the item can be dragged horizontally as well.

@R Sturim, thanks, I'll definitely consider working on some more basic ajax screencasts in the near future.

@Shreyans, Rails is doing some magic here, it parses the name of the migration and knows we're trying to add the position column to the faqs table. This was added in Rails 2.0, see the episode on that for details.
http://railscasts.com/episodes/83-migrations-in-rails-2-0


28. Stuart Feb 08, 2009 at 23:23

I'm currently working through implementing this on a has_many through association. Are there any tricks involved in doing that?


29. Stuart Feb 09, 2009 at 00:11

Sorted it (no pun intended, really). I had to do the for each on the association model in the middle. It looks like there are a few more db accesses to get anything from the models I really want though as each call to the associated model on the other side must get loaded afterwards.


30. Brad Robertson Feb 12, 2009 at 09:16

Do you know how to use this to sort by more than just position? I want to sort people with drag/drop, but maintain two columns, male and female. Can u sort by, say, position and sex?


31. Pablo Feb 23, 2009 at 02:29

Nice tutorial! :) I've been following these for a while.

However, is there any simple way to drag items between lists, or to drop an item into a dropbox?


32. Nadav Mar 08, 2009 at 15:16

Great tip !! Thanks.
However, I think that the sort action
could be made to be more efficient. If I understood right, it generates a separate update for each line, every time the user drags a faq. Better to generate a single update string and execute it after the loop (in the sort action).


33. George Mar 24, 2009 at 16:57

One of my favorite Rails episodes. Thanks.


34. Travis Apr 13, 2009 at 14:25

Using this method to update the positions of records, how do you get a sweeper to fire? The only way I've been able to do it is with this method:

http://gist.github.com/94735

Unfortunately, it fires once for every record that is updated. Is there a better way to get it to fire only once?


35. Rupert Sep 12, 2009 at 04:18

This is BRILLIANT - I've used it gadzillion times. More "simple" stuff like this please Ryan, I learn a ton of stuff each time.


36. Ben Carroll Nov 18, 2009 at 13:09

Holy spam!
But anyway I was wondering if you delete some records would it still be sortable?


37. bearblu Nov 19, 2009 at 14:11

Can this technique be combined with the dynamic list that you presented in #73? I would like to be able to add elements and sort them during creation and edit.


38. Ronald Evers Jan 06, 2010 at 03:47

For a jQuery-based alternative that works on trees instead of flat lists, see http://blog.ronaldevers.nl/posts/6-rails-sortable-element-helper-with-jquery

Great stuff Ryan!


39. javon Jan 25, 2010 at 22:36

Great stuff Ryan!

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source