Build Robust & Production Quality Applications - Lesson 1: Custom Form Builders

Let's say we have a Todo's app, where we allow our users to create a new todo. We also want to show errors when validation errors occur:

#new.html.haml
%section.new_todo
  %h3 Add a new todo
  = form_for @todo do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

The issue with this code above is that it is not pulling in the errors on the Todos object.

We could:

#new.html.haml
%section.new_todo
  %h3 Add a new todo
  = form_for @todo do |f|
  if@todos.errors?
    = f.label :name, "Name"
    = f.text_field :name

...but this is cumbersome to do for each form.

Lets create a helper called MyFormBuilder that will customize the label:

class MyFormBuilder < ActionView::Helpers::FormBuilder
  def label(method, text = nil, options = {}, &block)
    errors = object.errors[method.to_sym]
    if errors
      text += " <span class =\"error">#{errors.first}</span>" #pull the first error from the errors object (an array)
    end
    super(method, text.html_safe, options, &block) #delegates control back to the custom form builder
    end
  end

Update new.html.haml, make sure you add "MyFormBuilder" to your form_for block:

%section.new_todo
  %h3 Add a new todo
  = form_for @todo, builder: MyFormBuilder do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

Alternate Option: my_ form_ for helper

This is still somewhat repetitive. Let's create a my_ form_ for helper. We can manually merge in the myformfor rather passing in the MyFormBuilder:

app/helpers/my_form_builder.rb
module ApplicationHelper
  def my_form_for(record, options = {}, &proc) #takes 3 arguements
    form_for(record, options.merge!({builder: MyFormBuidler}), &proc)
  end
end

Update the new.html.haml

%section.new_todo
  %h3 Add a new todo
  = my_form_for @todo do |f|
    = f.label :name, "Name"
    = f.text_field :name
    = f.label :description, rows: 6
    %br
    = f.submit "Add This Todo"

Custom Form Builders in The Wild!

1. Formtastic

2. Simple_form (creators of devise)

3. BootstapForm