But instead of putting the logic into a Helpers method or Decorator class, moving it into a customized FormBuilder is worth a consideration.
Especially when it:
- is tied to a form
- is repeated
- generates tags (or even multi tag widgets)
<% form_for @recipe do |f| %>
<% (1..10).each do |rating| %>
<%= f.radio_button :rating, rating %>
<%= f.label "rating_#{rating}", rating %>
<% end %>
<% end %>
What if the view could be refactored to:
<% form_for @recipe, builder: Forms::CollectionFormBuilder do |f| %> <%= f.labeled_radio_button_group 1..10, :rating %> <% end %>Please note the defined option builder:, pointing to the customized FormBuilder.
The better readability is obvious, aside from the less coding. It can be achieved by creating a new class (collection_form_builder.rb) in lib/forms:
module Forms
class CollectionFormBuilder < ActionView::Helpers::FormBuilder
def labeled_radio_button_group collection, method, options={}
collection.inject(''.html_safe) { |html, value|
checked = object.send(method).eql? value
options[:id] = "#{method}_#{value}"
html += @template.radio_button_tag("#{@object_name}[#{method}]", value, checked, options) +
@template.label_tag(options[:id], value)
}
end
end
end
by inheriting from ActionView::Helpers::FormBuilder. Thus some instance variables stated by FormBuilder#form_for are available:
- @object (the object assigned to form_for itself, like @recipe)
- @object_name (the objects name, like "recipe")
- @template (the current view (an instance of ActionView::Base); this object provides all methods available in your view)
- @options (the options assigned to form_for)
- @proc (the block assigned to form_for)
@template.render partial: "fancy_widget",
locals: { object: @object }
Besides overwriting existing FormBuilder methods can make sense:
module Forms
class LabeledFormBuilder < ActionView::Helpers::FormBuilder
def check_box method, options={}, checked_value="1", unchecked_value="0"
text = options.delete :text
@template.content_tag(:label) {
super(method, options, checked_value, unchecked_value) +
(text or checked_value).to_s.html_safe
}
end
end
end
which generates the typical check box tag embraced by a label tag:
<% form_for @recipe, builder: Forms::LabeledFormBuilder do |f| %> <%= f.check_box :published, text: 'Published', id: nil %> <% end %>resulting in HTML:
Further articles of interest:
Supported by Ruby 2.1.1 and Ruby on Rails 4.1.8

Keine Kommentare:
Kommentar veröffentlichen