Playing with FormBuilder

The methods that come with the standard ActionPack::Helpers::FormBuilder cover most cases of what you need to do but if you need to do something it doesn't have a method for, things can get a little ugly.

In my case I wanted to create a helper for autocompleted fields that all had the same attributes but different arguments (the urls).

So let's say you have an Unobtrusive JavaScript file that looks for an attribute called data-autocomplete, normally when you wanted to have the field autocompleted, you would pass in the attribute to the form helper text_field.

f.text_field :username, :'data-autocomplete' => users_path(:json)

While there is nothing bad about doing this, you end with a bunch of :'data-autocomplete' scattered about in your views which makes it harder to change down the road if you were to switch your JS implementation.

A nicer way is to have your own form builder method called autocomplete_field which requires two arguments (method and url) that adds the :'data-autocomplete' option (which is now in one place and easily changed) for you. Best part is this can be done with only one helper file.

app/helpers/form_helper.rb

module FormHelper
 class CustomFormBuilder < ActionView::Helpers::FormBuilder
  def autocomplete_field(method, url, options = {})
   text_field(method, options.reverse_merge(:'data-autocomplete' => url)
  end
 end

 %w(form_for fields_for).each do |method|
  class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
   def custom_#{method}(record_or_name_or_array, *args, &proc)
    options = args.extract_options!
    options[:builder] = CustomFormBuilder
    #{method}(record_or_name_or_array, *(args << options), &proc)
   end
  RUBY_EVAL
 end
end

Now to use this new function, all you need to change is form_for into custom_form_for and the call itself.

<%= custom_form_for @user do |f| %>
 <%= f.label :username %>
 <%= f.autocomplete_field :username, user_path(:json) %>
<% end %>

While this is a greatly simplified example (and arguably over complicated), you can use this method to add or override any method safely for any FormBuilder in your application.

Leave a comment

Your email address will not be published. Required fields are marked *