For the last year I have been working on a large social network website.
As I preper to move forward I can't help thinking of the challenges that aroused during this time.
One in particular stood out. The form fields were heavily customised -
every field, be it a simple text or select, even file uploads were all
styled.
The design was less than ideal for a project this size to say the least.
While I didn't code the html I had to find a solution to integrate it that worked well and wouldn't prove to be time consuming.
The solution I chose was to extend the default form and use it across
the site. Even if these forms were to be styled with javascript it would
had just resulted in a poor user experience as each page form would
briefly be displayed naked then quickly styled.
Bear in mind that this code runs under Rails 2. It may require some adjustments to work with 3.
Firstly I defined my own form builder:
class CustomFormBuilder < ActionView::Helpers::FormBuilder
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
..
end
As it was going to be used sitewise I also made it default in my environment file:
ActionView::Base.default_form_builder = CustomFormBuilder
Each text field had to be enclosed in a specific span tag with the class
"input_text". Targeting with input[type="text"] would had probably
worked just as well if you ignore IE6.
def text_field(method, wrapper_options = {}, input_options = {})
wrapper_options[:class] = (wrapper_options[:class].to_s + ' input_text').strip
wrapper_options.reverse_merge!({ :class => 'input_text' })
content_tag :span, @template.send(:text_field, @object_name, method, objectify_options(input_options)), wrapper_options
end
You can style both the wrapper and the input field with separate option hashes.
The password, textarea and select fields were similarly styled. The
latter was albeit more difficult but I'm not going to post the code as
the markup differs significantly from one implementation to another.
After doing these I received an interesting request of extending the
label tag, having many fields that were mandatory and had to be
displayed accordingly.
def label(method, text = nil, options = {})
if options[:required]
text = (text || method.to_s.humanize) + ' <span class="highlight">*</span>'
end
super
end
To do the file uploads (which were surprisingly plentiful) I decided on
creating my own helpers, rather than just extending the basic "file".
This was mostly because I needed both an actual file upload and a photo
upload which had a very different behaviour such a live thumbnails. I
used flash (swf upload) because it was the only crossbrowser way of
selecting multiple files and styling the actual button.
def flash_photo_upload(name = 'files', options = {})
locals = {
:f => self,
:name => name.to_s,
:queue_limit => options['queue_limit']
}
@template.render :partial => 'partials/flash_file_upload', :locals => locals
end
There's a large amount of code responsible for the entire upload
process, that's why I isolated it in a partial. Why so much code you
ask, well for instance once one or more photos gets uploaded they're
displayed in a carousel; also take into consideration that it had to
work seamlessly when editing.
The final piece was styling the submit buttons (and also some links that
needed to look as buttons) - rounded cornered of different size and
backgrounds using a beautiful but proprietary font with a text shadow.
We had to support IE, so CSS3 was out of the question because we needed
the rounded corners which don't degrade very well. Legal font issues
aside, normally I would be doing this with the sliding doors css technique. I went the extra mile and generated the images in ruby using
the same css technique but with rmagick. The code partially supported
some css parameters to make styling easier.
I extracted that code and made it public as a gem named
magic_door. The helpers are packed separately
as a
plugin.
I hope this helps anyone who faces a similar situation.