Marcio Trindade

Formtastic

O formtastic é uma gem mantida pelo Justin French que pode ser considerado com um conjuto de helper que utilizam semântica HTML e facilita a criação de formulários em projetos Rails.

A instalação é bem simples, basta adicionar a linha abaixo no arquivo environment.rb.

environment.rb
config.gem 'formtastic'

então instalar as dependências com o rake do rails.

console
rake gems:install
script/generate formtastic

Pra montar um scaffold completo do seu modelo você pode usar os helpers em suas views:

_form.html.erb
<% semantic_form_for @user do |f| %>
  <%= f.inputs %>
  <%= f.buttons %>
<% end %>

No brwoser vai mostrar um html igual a este.

_form.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_name_input">
        <label for="user_name">Nome<abbr title="required">*</abbr></label>
        <input id="user_name" maxlength="255" name="user[name]" size="50" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

Algumas vezes queremos montar um formulário somente de alguns determinados campos do objeto então você pode especificar este da seguinte forma.

_form.html.erb
<% semantic_form_for @user do |f| %>
  <%= f.inputs :email, :password %>
  <%= f.buttons :commit %>
<% end %>
_form.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

Caso você queira ainda especificar algo pra cada um dos atributos você pode escrever um input pra cada atributo.

_form.html.erb
<% semantic_form_for @post do |f| %>
  <% f.inputs do %>
    <%= f.input :email %>
    <%= f.input :password %>
  <% end %>
  <% f.buttons :commit %>
<% end %>
_form.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_name_input">
        <label for="user_name">Nome<abbr title="required">*</abbr></label>
        <input id="user_name" maxlength="255" name="user[name]" size="50" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

Existem muitos outras opções pra utilizar :as, :collection, :input_html, :button_html, :wrapper_html, :label, :hint, :for e :required

A opção :as serve pra trocar o valor padrão de um determinado campo, abaixo a lista de valores possíveis.

exemples.rb
:select # um campo select. Padrão pra relacionamentos: belongs_to, has_many e has_and_belongs_to_many.
:check_boxes # um gropo de campos check_box. Alternativa para o :select que pode ser usado nos relacionamentos: has_many e has_and_belongs_to_many.
:radio # um grupo de campos radio. Alternativa para o :select que pode ser usado no relacionamento: belongs_to.
:time_zone # um campo select. Padrão pra colunas tipo: :string que tenha "time_zone" no nome.
:password # um campo password . Padrão pra colunas tipo: :string que tenham "password" no nome.
:text # um textarea. Padrão pra colunas tipo: :text.
:date # um select de datas. Padrão pra colunas tipo: :date.
:datetime # um select de data e hora. Padrão pra colunas tipo: :datetime e :timestamp.
:time # um select de hora. Padrão pra colunas tipo: :time.
:boolean # um checkbox. Padrão pra colunas tipo: :boolean.
:string # um campo de texto. Padrã pra colunas tipo: :string.
:numeric # um campo de texto (igual ao :string). Padrão para os camppos tipo: :integer, :float e :decimal.
:file # um campo file. Padrão pra atributos: paperclip or attachment_fu.
:country # um select com os nomes dos países. Padrão pra colunas com o tipo: :string com o nome "country" - precisa ter o plugin country_select instalado.
:hidden # um campo hidden. Padrão pra colunas tipo: :integer com nome :id.
exemple.html.erb
<% semantic_form_for @post do |f| %>
  <%= f.inputs do %>
    <%= f.input :email %>
    <%= f.input :roles, :as => :check_boxes %>
  <% end %>
  <% f.buttons :commit %>
<% end %>

HTML

exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="check_boxes optional" id="user_roles_input">
        <fieldset>
          <legend class="label"><label for="user_role_ids_1">Roles</label></legend>
          <ol>
            <li>
              <label for="user_role_ids_1">
                <input name="user[role_ids][]" type="hidden" value="" />
                <input id="user_role_ids_1" name="user[role_ids][]" type="checkbox" value="1" /> Admin
              </label>
            </li>
          </ol>
        </fieldset>
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :collection é usada pra alterar as opções de um campo select ou checkbox.

exemple.rb
f.input :authors, :as => :check_boxes, :collection => User.find(:all, :order => "last_name ASC")
f.input :authors, :as => :check_boxes, :collection => current_user.company.users.active
f.input :authors, :as => :check_boxes, :collection => [@justin, @kate]
f.input :authors, :as => :check_boxes, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
f.input :author,  :as => :select,      :collection => Author.find(:all)
f.input :author,  :as => :select,      :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
f.input :author,  :as => :select,      :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
f.input :author,  :as => :radio,       :collection => User.find(:all)
f.input :author,  :as => :radio,       :collection => [@justin, @kate]
f.input :author,  :as => :radio,       :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
f.input :author,  :as => :radio,       :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
f.input :admin,   :as => :radio,       :collection => ["Yes!", "No"]

A opção :input_html é usada pra passar atributos HTML para o campo.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :name,  :input_html => { :size => 100 } %>
    <%= f.input :email, :input_html => { :disabled => true } %>
    <%= f.input :senha, :input_html => { :class => 'senha' } %>
  <% end %>
  <%= f.buttons %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_name_input">
        <label for="user_name">Nome<abbr title="required">*</abbr></label>
        <input id="user_name" maxlength="255" name="user[name]" size="100" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" disabled="disabled" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input class="senha" id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :button_html é usada pra passaratributos HTML paraos botões.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <%= f.inputs %>
  <% f.buttons do %>
    <%= f.commit_button :button_html => { :class => "primary" } %>
  <% end %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_name_input">
        <label for="user_name">Nome<abbr title="required">*</abbr></label>
        <input id="user_name" maxlength="255" name="user[name]" size="50" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="primary create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :wrapper_html é usada pra passar atributos HTML para o elemento HTML que envolve os campos, por padrão uma tag <li>.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :email,    :wrapper_html => { :class => "email" } %>
    <%= f.input :password, :wrapper_html => { :style => "margin:2px;" } %>
  <% end %>
  <%= f.buttons %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required email" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input" style="margin:2px;">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :label é usada pra setar um label específico.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :email,    :label => 'Seu email:' %>
    <%= f.input :password, :label => 'Sua senha:' %>
  <% end %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_email_input">
        <label for="user_email">Seu email:<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Sua senha:<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :hint é usada pra passar texto de informação dentro do bloco.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :email,    :hint => 'Deve ser um email válido.' %>
    <%= f.input :password, :hint => 'Deve conter pelo menos 2 caracteres especiais.' %>
  <% end %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
        <p class="inline-hints">Deve ser um email válido.</p>
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
        <p class="inline-hints">Deve conter pelo menos 2 caracteres especiais.</p>
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :for é usada pra formulários com nested_attributes.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <%= f.inputs :name, :email %>
  <%= f.inputs :idade, :for => :perfil %>
<% end %>

uma alternativa é usar como bloco passando então a usar o helper semantic_fields_for.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :name %>
    <%= f.input :email %>
  <% end %>
  <% f.semantic_fields_for :perfil do |p| %>
    <%= p.input :idade %>
  <% end %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string required" id="user_name_input">
        <label for="user_name">Nome<abbr title="required">*</abbr></label>
        <input id="user_name" maxlength="255" name="user[name]" size="50" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="string required" id="user_perfil_age_input">
        <label for="user_perfil_age">Idade<abbr title="required">*</abbr></label>
        <input id="user_perfil_age" name="user[perfil][age]" size="50" type="text" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

A opção :required é usada para informar se o campo é obrigatório ou opcional.

exemple.html.erb
<% semantic_form_for @user do |f| %>
  <% f.inputs do %>
    <%= f.input :name,     :required => false %>
    <%= f.input :email,    :required => true %>
    <%= f.input :password, :required => true %>
  <% end %>
<% end %>
exemple.html
<form action="/admin/users" class="formtastic user" id="new_user" method="post">
  <div style="margin:0;padding:0;display:inline"><input name="authenticity_token" type="hidden" value="P0Wk/UO8rwgkhuhkEkz7sF1raZ9Xi/fvAx+Bhc/N3ko=" /></div>
  <fieldset class="inputs">
    <ol>
      <li class="string optional" id="user_name_input">
        <label for="user_name">Nome</label>
        <input id="user_name" maxlength="255" name="user[name]" size="50" type="text" />
      </li>
      <li class="string required" id="user_email_input">
        <label for="user_email">Email<abbr title="required">*</abbr></label>
        <input id="user_email" maxlength="255" name="user[email]" size="50" type="text" />
      </li>
      <li class="password required" id="user_password_input">
        <label for="user_password">Senha<abbr title="required">*</abbr></label>
        <input id="user_password" name="user[password]" size="50" type="password" />
      </li>
    </ol>
  </fieldset>
  <fieldset class="buttons">
    <ol>
      <li class="commit">
        <input class="create" id="user_submit" name="commit" type="submit" value="Create Usuário" />
      </li>
    </ol>
  </fieldset>
</form>

O formtastic continua crescendo a cada dia e existem mais coisas já implementas e que estão em implementação, então se você precisar de algo mais específico basta dar uma conferida no projeto que é bem provável você encontrar o que precisa.