h1

NestedSortable Rails Plugin

November 19, 2007

If you are a Ruby on Rails developer (if not you should become one) I have good news for you: a Rails plugin that makes it really easy to implement a NestedSortable Widget data source is almost ready for primetime. That will make implementing the NestedSortable Widget as easy as 1-2-3. It is already working, as a matter of fact, its only missing some automated tests, helpers I want to create (for including the NestedSortable widget in your views) and documentation. It works for Rails 1.2.3, I haven’t tested it with Rails edge yet.

I will give a brief overview of how it works. All you need is a model with a column that should hold a foreign key to the parent and a column to hold an index number for the position of the element inside its hierarchy. The defaults (convention over configuration!) are “parent_id” and “position”. Suppose our model (the one we want to nest and sort) is named “Item”. You will only need one line of code to implement a data source for it in your controller:

class ItemsController < ApplicationController
	nested_sortable_data_source_for :item
end

By default the data source will be created under an action named “order”, inside your controller. Don’t worry, all the defaults can be overridden (wait for the docs or take a peak at the code).

In your view, it will be pretty easy to add the NestedSortable Widget, as always:

<script> 	
	jQuery( function($) {
		$('#items_sort').NestedSortableWidget({
 			loadUrl: "/items/order/1.json"
 		});
	});
</script> 
<div id="items_sort"></div>

Don’t forget that you will need to include the scripts (jQuery, Interface, etc) and the CSS file for the widget, by putting something like this in the HEAD of your HTML file, in your layout file probably:

<%= stylesheet_link_tag 'ns/nestedsortablewidget' %>
<%= javascript_include_tag "jquery-1.1.4.js" %>
<%= javascript_include_tag "interface-1.2.js" %>
<%= javascript_include_tag "inestedsortable-1.0.1.pack.js" %>
<%= javascript_include_tag "jquery.nestedsortablewidget-1.0.pack.js" %>

Ok, you say. That is neat, but how do I customize what data will be displayed in the columns by my NestedSortable Widget? The default is to display all the columns/attributes your model has (excluding the ID and foreign keys). You can use the :columns option of the nested_sortable_data_source_for to make your data source display pretty much anything you want, in any order you want. Just an example:

nested_sortable_data_source_for(
	:item,
	:columns=>[
		[:title, 'Title of the Item'],
		[:description, 'Description of the Item'],
		[ lambda {|i| i.created_at.to_date.to_formatted_s(:long)}, 'Creation Date'],
		[ lambda {|i| i.attachments.count}, 'Attachments']
	]
)

If you are smart enough you already got it: it will display a table with 4 columns, the title of the item, description, creation date and the number of attachments it has. Note the use of lambdas (functions that are passed as variables). It gives you the power to display whatever you want in the columns of your NestedSortable Widget.

Ok again, you say. The way things are until now will only allow me to have one set of items per database table. In your application, you will usually have a lot of users, and will not want to create one table per user. You want everything in the same table, and your “item” model probably have a “user_id” foreign key. That has been thought of as well. You have the :conditions option, which will give you the flexibility you need. An example:

nested_sortable_data_source_for(
	:item,
	:conditions=> lambda {|controller| {:user_id => controller.params[:id]} }
)

Note the funky lambdas again. Instead of a lambda, you may pass in a symbol, like :conditions_generator with the name of a method in your controller (make it protected, please) that will generate the conditions for you. The conditions are exactly like the ones you may use in your finders in Rails: they may be strings with SQL, they may be arrays like ["a = ?", "b"], and anything else that is allowed in Rails conditions.

In your view, in the part you set up the widget, you would probably use something like:

loadUrl: "/items/order/<%=current_user.id%>.json"

This is insecure: users could easily change the order of the items that belong to other users, with little effort. To implement security, you need to make our condition generator a little bit smarter. It is up to you, though…

Briefly, that is what this plugin for Rails does: it makes it ridiculously easy to implement a NestedSortable Widget in your Rails apps (I bet you could do it in 5 minutes!). Don’t show it to your boss, he will probably fire you and begin doing it himself. Note that the data source you create fully supports pagination: you may use any option the NestedSortable Widget provides and it should work like a charm.

To try the plugin, install it using Rails install command, in your Rails application path:

script/plugin install https://nestedsortables.googlecode.com/svn/trunk/rails/nested_sortable

About these ads

6 comments

  1. Great work with this plugin do you intend to port this over toe JQuery UI the current one is nowhere near as good


  2. ¡What a great plugin for Rails!

    But I have some little questions unsolved. I’d like to link database update to the onChange event, but I don’t know what function call.

    Another great thing to implement would be to eliminate the default buttons for updating data.

    Which is the method to get this?

    Thank you very much for your great work.

    Greets.


  3. DaVinci: Right now I am very busy with other projects and am not giving NestedSortables any love. Eliminating the save buttom is no rocket science, but you need to do change the javascript code yourself, since I didn’t add this option in NestedSortable Widget. This will generate a server call every time the user drops an item, it might be a bad thing.

    I suggest you use Firebug to help you follow the code.


  4. Thank you for your response. What bad notices, if you doesn’t work anymore in NestedSortables :(

    I have seen your JS code, so I make an idea of how to change it now. Only wanted to know if some hidden option could help me.

    Thank you again and luck with your other projects.

    Greets.


  5. I have a little question with :columns. If you were so kind by guiding me throw the good way… ;)

    Problem is how to define a lambda that see the controller environment. I need to define some links in each register (update, delete…) but I can’t use link_to, or REST helpers from lambdas in :columns definitions. The only argument for lambda is The record from model, so how can I use controller/view helpers or Rails? Perhaps you have not taken care of this posibility?

    Help.

    Thank you.


  6. DaVinci: You are right, I haven’t thought about it.

    I believe the possibilities are either passing the controller option as an argument for the lambdas or using instance_eval to evaluate the lambda in the same context of the controller (it would be a better option I guess).

    Search for “# lambdas can be passed as the column data, they will be called” in the plugin code, under the ‘lib’ subdir, that will guide you to the line that needs to be altered. If you manage to add this feature, send it to be and I will add to the SVN.



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: