Custom Form Builders Are Evil

Every Formbuilder i see does the same, building some rows/tables/div structure around the fields it is be called on.

f.text_field => tr td label input

In theory this sounds great, but as soon as one tries to make some real life use of it it breaks down:

  • How do i handle I18N ?
  • How can i change the label text from the default
  • How do i put the checkbox in front of the label
  • How can i output only a field/2 fields in one row

Form builders that always output label+input are unsuited for everyday development!

So what else can we do ?

#app/helpers/application_helper.rb
class ActionView::Helpers::FormBuilder
  def build_translated_label(content)
    if content.class == Array
      label(content[0],content[1])#label + text
    elsif content.class == String
      label('',content)#text
    else content.class == Symbol
      #uses gettext translation, feel free to insert your own...
      label(content,_("#{object.class}|#{content.to_s.capitalize.gsub('_',' ')}"))#label + translation        
    end
  end
  
  def row(label,content)
    @template.content_tag('div',build_translated_label(label) + content.to_s,:class=>'row')
  end
end

Usage

f.row(_('Movie|Website')+'http://', f.text_field(:website))
f.row(:tags, f.text_field(:tag_list)+_('comma divided'))

If someone wants the R-specs, drop me a mail 🙂

8 thoughts on “Custom Form Builders Are Evil

  1. I wrote a form builder that’s similar to the ones you complain about, but overrides the field methods to allow you to specify the label:

    f.text_field :website, :label => ‘Your Web Site URL’

    That would wrap the field and label in a TR or a LI or whatever the site demands. To handle extra info before or after the input field, I’d just add another option, like :info => ‘(comma divided)’. Moving the checkbox could use a similar option.

    I also add a bunch of methods for type-specific fields, like name, integer, phone_number, zip_code, etc. These are generally just text fields with classes added to allow client-side JavaScript to validate the input. I also have a :required => true option to pass that info along to the client code.

    Interestingly, you can easily write Builder classes to take that same code and build show or index views. I plan to blog about this soon, and present it to my local RUG.

    That covers everything except putting multiple items in a row. Your idea for rows is good, but I don’t think it goes far enough. I think it’d be better to create a row builder (and only use it for situations where 1 field per row doesn’t hold true). So you’d do something like this:

    f.row do |r|
    r.name :name_first, :label => ‘First Name’
    r.name :name_last, :label => ‘Last Name’
    end
    f.address :address
    f.row do |r|
    r.name :city
    r.name :state, :size => 2
    r.zip_code :zip_code
    end

  2. Great response 🙂
    I definitely like special fields like zip_code etc, that is imo what form builders should be used for.

    Adding more options kind of solves the shortcoming, but i still find the f.row approach easier to write/understand, everyone instantly knows what is going on, and how to hack things(change the label/order/class of label etc).

    I used to have some of these high-level helpers, but since no other team member understood/used them properly we always ran into errors/”how-do-i-do-that-again”s.

  3. Sounds like the presenter pattern would be a great idea for custom cases like you are describing. Much easier to write a custom class and a to_s method. Then in your view @my_presenter.to_s and presto!

  4. @Tim: I’ve tried using the presenter pattern for things like this. It doesn’t really solve the problem that Pragmatig is trying to solve. You’d just be moving the problem from the view (or helper) into the presenter.

    What the presenter can help with is cleaning up the interface between the view and the model. Just like helpers clean up the interface between the view and the controller.

  5. I agree with craig, i cannot see how a presenter would make this any simpler. My goal is to satisfy my clients crazy-form-needs with as less simple code as possible.

  6. I’m awfully late to the party here, only just stumbled across this post. I’m the author of the first example you link to and I’d just like to point out that only one of your points is valid for my implementation (multiple fields per row). There aren’t many scenarios where I can think that is desirable apart from checkboxes/radio buttons… which I’ve also accounted for. Doing it with text fields will mean that the label will be semantically incorrect, so you’re on your own.

    But I think you’re missing the point slightly, these aren’t meant to be a total replacement. Much like the way make_resourceful takes care of the heavy lifting for for RESTful controllers in a good percentage of cases, custom form builders are there to stop you having to repeat the same consistent semantic markup across your site. In the (hopefully edge) cases where that isn’t true, then you can either manually deal with it with your templating language or extend the builder.

  7. Maybe i put it the wrong way, my point is that a form-builder should only support, not dictate what you can do (e.g. by overwriting common methods). It should on the other hand, as you pointed out, provide the programmer with a simple way to build dry forms.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s