Sunday, 6 November 2011

Bits of Code #2 - Ordering records through index page with AJAX (powered by jQuery) in a Rails 3.1 App

In one of the projects that I worked in I had to make it possible for users to easily change the order in which the records of a table would appear in their index page. I knew that there were a few gems out there that could handle this for me, but I thought that it was such a simple functionality that it was probably not a strong enough reason to add another dependency to the project; so I went about building it.

In this example I've added this functionality to a Product model. I started by creating a migration to add the column that would keep the order between products, I decided to call this column sort_index.

Then I added a few methods to the Product model. Some methods to check the position of a product, others to move the product up and down in the 'ranking' of products, and a before_create callback to set the initial sort_index of each new product. Note that the upper position in my implementation is the position with sort_index equal to 0, so moving a product up is actually reducing its sort_index by 1.

These methods were tested with RSpec (might not be 100% coverage, let me know if that is the case). I've moved the RSpec file to the end of the post because is quite a long file.

Having these methods working I implemented the functionality and the interface to allow the users to actually change the ordering of the products.

Created the necessary routes.

Created the controller methods.

Followed by the views. The index page.

Which calls the _ordering partial

And then the js views to process the result of the requests. To move down.

And to move up.

If you need to use this in more than one model you can move some bits into a Module and then include the Module where necessary. I haven't had the need to do it. But if I do, I'll probably do a post about it.

Hope these bits of code are useful for you some day, I've already used in two projects. If you find any issues or have any suggestions to improve this code please let me know.

And now for the RSpec file:

Friday, 4 November 2011

Bits of Code - Nested Attributes

Yesterday I had the need to implement a form with some nested attributes and so I did what I often do: I went back to a previous project and copy&pasted the necessary code and then changed it to match the new project's needs.

While I was doing this I thought that it would be useful to get this, and other bits of code in a place that I can easily refer to, instead of having to remember in which project I've used what. This is the reasoning behind this post.

Bare in mind that this is not a post about life changing bits of code, but rather bits of code that I need now and then and that are easier to just copy&paste&edit than to have to rewrite from scratch. Some of these bits can be easily found in different websites, but having them here will make my life easier than having to search again. I'll make sure I'll refer to the websites that I've used.

So these first bits of code are meant to build a form with nested attributes.

Imagine that you have a model named Content that has many Attachments and when you are adding or editing a content, you want to be able to manage that content's attachments. This allows you to easily do it.

This code uses Rails accepts_nested_attributes_for and then it manages the different attachments with javascript methods (using jQuery framework), allowing you to add, remove and edit them accordingly.

The Content model:

The Attachment model (notice the accepts_nested_attributes_for method):

The ApplicationHelper, with the methods used in the views:

Part of the Content form view (notice the use of the method fields_for, and the helper method link_to_add_fields):

The attachment_fields partial (notice the use of the helper method link_to_remove_field):

And the javascript methods (notice that if the position of the elements in the attachment_fields partial changes this code might need to be updated):

 These bits of code are based on (or were copied from):