#311 Form Builders pro
Forms often follow a similar pattern with a lot of repetition. Learn how to clean up form views and remove duplication with the help of form builders.
- Download:
- source codeProject Files in Zip (89.7 KB)
- mp4Full Size H.264 Video (26.6 MB)
- m4vSmaller H.264 Video (14.3 MB)
- webmFull Size VP8 Video (17.1 MB)
- ogvFull Size Theora Video (32.7 MB)
Resources
views/products/_form.html.erb
<%= labeled_form_for @product do |f| %> <%= f.error_messages %> <%= f.text_field :name %> <%= f.text_field :price, label: "Unit Price" %> <%= f.text_area :description, rows: 8 %> <%= f.check_box :discontinued %> <h2>Categories</h2> <%= f.collection_check_boxes :category_ids, Category.all, :id, :name %> <%= f.submit %> <% end %>
application_helper.rb
def labeled_form_for(object, options = {}, &block) options[:builder] = LabeledFormBuilder form_for(object, options, &block) end
app/form_builders/labeled_form_builder.rb
class LabeledFormBuilder < ActionView::Helpers::FormBuilder delegate :content_tag, :tag, to: :@template %w[text_field text_area password_field collection_select].each do |method_name| define_method(method_name) do |name, *args| content_tag :div, class: "field" do field_label(name, *args) + tag(:br) + super(name, *args) end end end def check_box(name, *args) content_tag :div, class: "field" do super + " " + field_label(name, *args) end end def collection_check_boxes(attribute, records, record_id, record_name) content_tag :div, class: "field" do @template.hidden_field_tag("#{object_name}[#{attribute}][]") + records.map do |record| element_id = "#{object_name}_#{attribute}_#{record.send(record_id)}" checkbox = @template.check_box_tag("#{object_name}[#{attribute}][]", record.send(record_id), object.send(attribute).include?(record.send(record_id)), id: element_id) checkbox + " " + @template.label_tag(element_id, record.send(record_name)) end.join(tag(:br)).html_safe end end def submit(*args) content_tag :div, class: "actions" do super end end def error_messages if object.errors.full_messages.any? content_tag(:div, :class => "error_messages") do content_tag(:h2, "Invalid Fields") + content_tag(:ul) do object.errors.full_messages.map do |msg| content_tag(:li, msg) end.join.html_safe end end end end private def field_label(name, *args) options = args.extract_options! required = object.class.validators_on(name).any? { |v| v.kind_of? ActiveModel::Validations::PresenceValidator } label(name, options[:label], class: ("required" if required)) end def objectify_options(options) super.except(:label) end end