stouset/twitter_bootstrap_form_for

Custom form helpers results in multiple labels

jperrine opened this issue · 5 comments

I have a custom form field that I created that just passes off a value and class to a text field and when using it in a twitter_bootstrap_form_for it results in a label being wrapped around the element twice.

The code for my form field is the following:

def date_field(field_name, index, options={})
  options.merge!(:value => object.send(field_name).to_s, :class => "datePicker")

  text_field field_name, options
end

and the code for my form is the following:

= twitter_bootstrap_form_for([@event, @day]) do |f|
  = f.inputs 'Day' do
    = f.text_field :name
    =f.date_field :date
  = f.actions do
    = f.submit "Save"
    = button_tag 'Cancel', :type => 'reset', :class => 'btn'

And the output HTML looks like this:

<div id="day_date_input" class=" clearfix">
  <label for="day_date">Date</label>
  <div class="input">
    <div id="day_date_input" class=" clearfix">
      <label for="day_date">Date</label>
      <div class="input">
        <input type="text" value="12/22/2011" size="30" name="day[date]" id="day_date" class="datePicker hasDatepicker"></div>
      </div>
    </div>
  </div>
</div>

Am I doing something wrong or is this an issue with using custom form helpers with this gem?

This is an unfortunate side effect of how I detect form helpers.

INPUTS = [
  :select,
  *ActionView::Helpers::FormBuilder.instance_methods.grep(%r{
    _(area|field|select)$ # all area, field, and select methods
  }mx).map(&:to_sym)
]

It automatically defines a wrapper around any helper named select, or ending in _area, _field, or _select. This includes your custom helper. So your helper gets a label, then calls another _field method which gives it a second label.

I'm not sure this is something I want to change, since it's a pretty small outside case. Doing it this way gives the plugin a high likelihood of catching any new form helpers Rails adds in the future (as they've done recently for HTML5 helpers). I'm open to suggestion, though.

In the meantime, you have a few options. You can define your helper the way Rails does, which doesn't involve a call to another _field method:

def text_field(object_name, method, options = {})
  InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
end

Another option is, I think, to call the method on the FormBuilder's template (this is just a guess, it may or may not work, and it does look kind of crappy):

= twitter_bootstrap_form_for([@event, @day]) do |f|
  = f.inputs 'Day' do
    = f.         text_field :name
    = f.template.date_field :date

Let me know if either of those solutions work for you.

Cool, thanks for the suggestions it now looks good

Which approach did you use?

I wasn't able to get the InstanceTag solution to work quite right so I went with the following:

module FormBuilder
  def date_field(method, options={})
    options.merge!(:value => @object.send(method).to_s, :class => "datePicker")
    @template.text_field(@object_name, method, objectify_options(options))
  end
end

ActionView::Helpers::FormBuilder.send(:include, FormBuilder)

Great!