Sunday 22 January 2012

Web-app-theme Gem

I came across a great gem today for creating prototype web applications - it's called web-app-theme. Typically when prototyping a new app, I have found myself spending an undue amount of time integrating a pre-made theme from the likes of Themeforest; this gem looks like it will change that, and make my prototyping much more efficient. From the blurb on the Github page, it looks to have been based around themes used on apps like Basecamp, which are very nice, well thought out layouts - perfect for prototyping (though I would still be ensuring that my design is bespoked before going into production).

Unfortunately it looks like there has not been a whole lot of development going on recently from the creator, though there are plenty of pull requests on the repository that fix most of the issues with Rails 3.1.

Some of the issues I have found (so far) using the master branch are:

  • Javascript_include_tag in the application layout file is wrong - needs to be changed to:
    <%= javascript_include_tag "application" %>

  • Image files are not in the correct location - can be corrected by:
    cp -r $(bundle show web-app-theme)/spec/dummy/public/images/* app/assets/images/web-app-theme/

I'm sure I will come across others, so I'll update here with my fixes when I do. Once Rails 3.2 goes into full release, no doubt there will be more breakages, so hopefully pilu resumes development soon.

Friday 20 January 2012

Rails 3.1 Linked Dropdown / Cascading Select

I'm currently using Rails 3.1, and I've found that although Rails has helper methods for a huge number of things, it does not have anything to help me create linked dropdowns (I've seen these referred to as cascading dropdowns and linked selects also), despite this being something I use in a lot of projects.

I created a solution previously that was very flexible, which involved using the jQuery Flexbox plugin and making AJAX calls when a selection changes, which returned JSON objects to populate child menus.  While this would have worked in my current project, it would be overkill, so I set about looking for a solution that is a little simpler, and came up with something that I will certainly want to use again.

The solution I have implemented here involves the jQuery Chained plugin, and a little work in my controller and views to get this working in Rails 3.

I first copied the plugin to my assets/javascripts directory, to include it in the project (no need to include it specifically in Rails 3.1, the application.js manifest will do that for us).

I should point out here that my current project is a home automation controller, so the simplest way to demonstrate this is with my Room and Device models (a Room has_many devices, and a Device belongs_to a Room). As such, I want to first have the user select a room, and then have the second dropdown only display devices that are in that room.

In my view, I created the room and device select boxes:

<%= select("room", "name", options_for_select(@rooms_for_dropdown), {:prompt => 'Select Room'}) %>

<%= select("device", "name", options_for_select(@devices_for_dropdown), {:prompt => 'Select Device'}) %>

In my controller, i created some of the variables I used above.

@rooms = Room.all
@rooms_for_dropdown = []
@rooms.each do |i|
  @rooms_for_dropdown = @rooms_for_dropdown << [i.name,i.id]
end
    
@devices = Device.all
@devices_for_dropdown = []
@devices.each do |i|
  @devices_for_dropdown = @devices_for_dropdown << [i.name,i.id,{:class => i.room.id}]
end

You will note that I've used :class in the array of devices that I'm returning - this is essential for using the jQuery chained plugin. What is happening is that we are actually returning an array of all devices available to the user, but each of the options has a class according to what room it belongs to. This allows the plugin to dynamically hide the options that are not relevant, without making an AJAX call (it should be noted that if Javascript happens to be turned off, all options will be available - while this wouldn't be ideal, it means that the application won't be completely broken without JS).

The last thing I need to do is tell the plugin what select boxes are chained, which I can do in a file called jquery.chained_dropdowns.js (again, no need to specifically include this file in Rails 3.1, but be careful of the naming - we need to ensure that it is included after jQuery and the jQuery chained plugin, and Rails 3.1 includes the files alphabetically, so calling it chained_setup.js, for example, would result in an error):

$(document).ready(function(){
 $('select#device_name').chainedTo('select#room_name');
});

That's it! Easy, wasn't it? One thing to note here is that we are including all of the available options in the html - if you have an enormous number of options (or if you don't want users to be able to pick up your complete option list), this might cause an issue - I'll come to this in a later post.

Why I made this blog

I'm a hobbyist developer, currently working with Rails, home automation protocols, jQuery and VBA (amongst others, but these are the main ones).

I've set up this blog mostly so that I can remember how to do something that is done infrequently, and that is a bit unusual - I often find that I know I have done something before, but that it was so long ago I have forgotten how I did it. If it turns out to be helpful to others, then that is a bonus!

I hope you enjoy.