Sexy Forms in Rails
Posted by glenn on Wednesday, July 16, 2008
Posted in forms, usability, accessibility, rubyonrails
I've been meaning to release this for quite a while now, I've finally got around to packaging it up for some form of public consumption. Basically, I got sick of having to manually create label tags for each of my form inputs. They should be there by default so that users with visual or other impairments have enough additional information to use the site. I'm also lazy, and would have a tendency to forget to put them in otherwise. I also wanted a consistent way to display any additional contextual information, so here it is.
I'll go through the code in detail here, but I'll keep it updated with any changes and available for download at the semantic form builder git repo. So now on to the code.
It started with a custom FormBuilder.
Personally I think they are a terribly underused aspect of the Rails framework. Anyway, stepping through what we've got here. We inherit from ActionView::Helpers::FormBuilder and then begin overriding the various tags that will be called within a form_for. All we do for most methods is call field_settings followed by wrapping. In the method field_settings we set the name of the field to the standard rails convention like person_first_name (@object is created for us automatically by form_for so we don't need to worry about it here). Next we set the label text to display for this form input, it will default to something sensible (generally the name of the field you are generating the input for) or you can override it per input by passing :label in as an option. And finally, we check if you have passed :required => true in and if so we set a required class so we can style it differently. All the relevant info gets returned, so we can pass it into the wrapping method.
So where exactly is wrapping? I've moved it out into a helper, as I wanted to use this functionality in regular form_tag calls as well as form_for. So take a look in semantic_form_helper:
There are 3 cases to look at initially. Wrapping a normal input, wrapping a boolean input (like a check box), and wrapping a group of check boxes. For wrapping a normal input, we create a label on the left and insert the field into a div on the right. I've got the boolean one there for the 'remember me?' type of scenario on a login page, where it looked to make more sense to have the label to the right of the checkbox rather than the left. And finally, wrapping a group of elements which isn't handled at all well in the current Rails setup. Overwhelmed yet? Let's just look at how you use it then:
You'll see we can pass in :required => true to visually identify required fields to the user. If we don't like the form name (like the generic 'list' attribute, then we can override with :label =>. And if there is additional contextual help to provide, we've got :help =>. We've also got a pretty way of showing a selection of check boxes with check_box_tag_group, but you'll have to specify the field name yourself. I've also put a convenience method in called submit_and_cancel that takes the text for a submit, and cancel button respectively. Catching the cancel action is up to you though.
And voila! The finished product. Any problems with it, please let me know or fork and fix/pull request on github. I've tested it on Safari 3, Firefox 2/3, and IE7. No idea what it looks like on IE6 just yet.
UPDATE: It's now a plugin, sexy and semantic forms are now available as a plugin