BLOG

Customising form controls in Rails

February 3rd, 2008, Programming, Ruby on Rails

Here is a simple way to implement custom form controls while still using Rails’ standard helpers. For this example, we’ll implement a two-state graphical alternative to a check-box (usually implemented by the operating system and uncustomisable via CSS), which represents a standard boolean field in your database model.

Normally we’d do something like in our view:

    <%= form_for @model, :url => { :action => :create } do |f| %>
        <%= f.check_box :someBool %>
        <%= f.submit("OK") %>
    <% end %>

But we’d like to use something a little like this to represent our field instead:

Active
Inactive

Firstly the images are added to our Rails view via the helpers, with the addition of assigning each one an ID and an inline style to hide the one opposing the required default state:

    <%= image_tag("signUpItemActive.gif", :size => "13x18",
        :alt => "Active", :id => "checkActive") %>
    <%= image_tag("signUpItemInActive.gif", :size => "13x18",
        :alt => "Inactive", :id => "checkInactive",
        :style => "display:none;") %>

These are then both wrapped in a single anchor tag, which is given an onclick handler to a JavaScript function.

    <a href="#" onclick="toggleCheckBox(); return false;">
         <%= image_tag("signUpItemActive.gif", :size => "13x18",
            :alt => "Active", :id => "checkActive") %>
         <%= image_tag("signUpItemInActive.gif", :size => "13x18",
            :alt => "Inactive", :id => "checkInactive",
            :style => "display:none;") %>
    </a>

The next step is to modify our standard Rails form to assign the check-box an ID, and hide it with some more inline CSS styling. This means it won’t be visible, but will continue to send its value when the form is submitted:

    <%= f.check_box :someBool, :id => "hiddenCheckBox", :style => "display:none;" %>

The JavaScript function can then be defined in Application.js (this could of course be modified to take the IDs as parameters):

    function toggleCheckBox()
    {
        elementActive = document.getElementById('checkActive');
        elementInactive = document.getElementById('checkInactive');
        elementHidden = document.getElementById('hiddenCheckBox');
 
        if (elementActive && elementInactive && elementHidden)
        {
            var state = (elementActive.style.display == "none");
            elementHidden.checked = state;
            elementActive.style.display = state ? "inline" : "none";
            elementInactive.style.display = state ? "inline" : "none";
        }
    }

There we go! Clicking the dummy check-box graphic will toggle the images and set the value in the hidden check-box at the same time. There is only one remaining step and that is to set the default state of the images to match the existing value of the boolean field. Without this the graphics and the form are likely to get out of sync. Even if the form is for creating a new record, the page will be shown again in the event of a validation error.

Another JavaScript function can take care of this:

  function setCheckBox()
    {
        elementActive = document.getElementById('checkActive');
        elementInactive = document.getElementById('checkInactive');
        elementHidden = document.getElementById('hiddenCheckBox');
 
        if (elementActive && elementInactive && elementHidden)
        {
            var state = elementHidden.checked;
            elementActive.style.display = state ? "inline" : "none";
            elementInactive.style.display = state ? "inline" : "none";
        }
    }

Then call this from the page’s body onLoad event:

    <body onload="setCheckBox();">

Leave a Reply