Module: Formtastic::Helpers::InputsHelper

Includes:
LocalizedString
Included in:
FormBuilder
Defined in:
lib/formtastic/helpers/inputs_helper.rb

Overview

#inputs is used to wrap a series of form items in a <fieldset> and <ol>, with each item in the list containing the markup representing a single #input.

#inputs is usually called with a block containing a series of #input methods:

<%= semantic_form_for @post do |f| %>
  <%= f.inputs do %>
    <%= f.input :title %>
    <%= f.input :body %>
  <% end %>
<% end %>

The HTML output will be something like:

<form class="formtastic" method="post" action="...">
  <fieldset>
    <ol>
      <li class="string required" id="post_title_input">
        ...
      </li>
      <li class="text required" id="post_body_input">
        ...
      </li>
    </ol>
  </fieldset>
</form>

It's important to note that the semantic_form_for and #inputs blocks wrap the standard Rails form_for helper and FormBuilder, so you have full access to every standard Rails form helper, with any HTML markup and ERB syntax, allowing you to "break free" from Formtastic when it doesn't suit:

<%= semantic_form_for @post do |f| %>
  <%= f.inputs do %>
    <%= f.input :title %>
    <li>
      <%= f.text_area :body %>
    <li>
  <% end %>
<% end %>

See Also:

Constant Summary

SKIPPED_COLUMNS =

Which columns to skip when automatically rendering a form without any fields specified.

[:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]

Instance Method Summary (collapse)

Methods included from LocalizedString

#model_name

Instance Method Details

- (Object) inputs(*args, &block)

#inputs creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc), or with a list of fields (accepting all default arguments and options). These two examples are functionally equivalent:

# With a block:
<% semantic_form_for @post do |form| %>
  <% f.inputs do %>
    <%= f.input :title %>
    <%= f.input :body %>
  <% end %>
<% end %>

# With a list of fields (short hand syntax):
<% semantic_form_for @post do |form| %>
  <%= f.inputs :title, :body %>
<% end %>

# Output:
<form ...>
  <fieldset class="inputs">
    <ol>
      <li class="string">...</li>
      <li class="text">...</li>
    </ol>
  </fieldset>
</form>

Quick Forms

Quick, scaffolding-style forms can be easily rendered for rapid early development if called without a block or a field list. In the case an input is rendered for most columns in the model's database table (like Rails' scaffolding) plus inputs for some model associations.

In this case, all inputs are rendered with default options and arguments. You'll want more control than this in a production application, but it's a great way to get started, then come back later to customise the form with a field list or a block of inputs. Example:

<% semantic_form_for @post do |form| %>
  <%= f.inputs %>
<% end %>

Nested Attributes

One of the most complicated parts of Rails forms comes when nesting the inputs for attrinbutes on associated models. Formtastic can take the pain away for many (but not all) situations.

Given the following models:

# Models
class User < ActiveRecord::Base
  has_one :profile
  accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
  belongs_to :user
end

Formtastic provides a helper called semantic_fields_for, which wraps around Rails' built-in fields_for helper for backwards compatibility with previous versions of Formtastic, and for a consistent method naming API. The following examples are functionally equivalent:

<% semantic_form_for @user do |form| %>
  <%= f.inputs :name, :email %>

  <% f.semantic_fields_for :profile do |profile| %>
    <% profile.inputs do %>
      <%= profile.input :biography %>
      <%= profile.input :twitter_name %>
    <% end %>
  <% end %>
<% end %>

<% semantic_form_for @user do |form| %>
  <%= f.inputs :name, :email %>

  <% f.fields_for :profile do |profile| %>
    <% profile.inputs do %>
      <%= profile.input :biography %>
      <%= profile.input :twitter_name %>
    <% end %>
  <% end %>
<% end %>

#inputs also provides a DSL similar to fields_for / semantic_fields_for to reduce the lines of code a little:

<% semantic_form_for @user do |f| %>
  <%= f.inputs :name, :email %>

  <% f.inputs :for => :profile do %>
    <%= profile.input :biography %>
    <%= profile.input :twitter_name %>
    <%= profile.input :shoe_size %>
  <% end %>
<% end %>

The :for option also works with short hand syntax:

<% semantic_form_for @post do |form| %>
  <%= f.inputs :name, :email %>
  <%= f.inputs :biography, :twitter_name, :shoe_size, :for => :profile %>
<% end %>

#inputs will always create a new <fieldset> wrapping, so only use it when it makes sense in the document structure and semantics (using semantic_fields_for otherwise).

All options except :name, :title and :for will be passed down to the fieldset as HTML attributes (id, class, style, etc).

When nesting inputs() inside another inputs() block, the nested content will automatically be wrapped in an <li> tag to preserve the HTML validity (a <fieldset> cannot be a direct descendant of an <ol>.

Examples:

Quick form: Render a scaffold-like set of inputs for automatically guessed attributes and simple associations on the model, with all default arguments and options

<% semantic_form_for @post do |form| %>
  <%= f.inputs %>
<% end %>

Quick form: Skip one or more fields

<%= f.inputs, :except => [:featured, :something_for_admin_only] %>
<%= f.inputs, :except => :featured %>

Short hand: Render inputs for a named set of attributes and simple associations on the model, with all default arguments and options

<% semantic_form_for @post do |form| %>
  <%= f.inputs, :title, :body, :user, :categories %>
<% end %>

Block: Render inputs for attributes and simple associations with full control over arguments and options

<% semantic_form_for @post do |form| %>
  <%= f.inputs do %>
    <%= f.input :title ... %>
    <%= f.input :body ... %>
    <%= f.input :user ... %>
    <%= f.input :categories ... %>
  <% end %>
<% end %>

Multiple blocks: Render inputs in multiple fieldsets

<% semantic_form_for @post do |form| %>
  <%= f.inputs do %>
    <%= f.input :title ... %>
    <%= f.input :body ... %>
  <% end %>
  <%= f.inputs do %>
    <%= f.input :user ... %>
    <%= f.input :categories ... %>
  <% end %>
<% end %>

Provide text for the <legend> to name a fieldset (with a block)

<% semantic_form_for @post do |form| %>
  <%= f.inputs :name => 'Write something:' do %>
    <%= f.input :title ... %>
    <%= f.input :body ... %>
  <% end %>
  <%= f.inputs do :name => 'Advanced options:' do %>
    <%= f.input :user ... %>
    <%= f.input :categories ... %>
  <% end %>
<% end %>

Provide text for the <legend> to name a fieldset (with short hand)

<% semantic_form_for @post do |form| %>
  <%= f.inputs :title, :body, :name => 'Write something:'%>
  <%= f.inputs :user, :cateogies, :name => 'Advanced options:' %>
<% end %>

Inputs for nested attributes (don't forget accepts_nested_attributes_for in your model, see Rails' fields_for documentation)

<% semantic_form_for @user do |form| %>
  <%= f.inputs do %>
    <%= f.input :name ... %>
    <%= f.input :email ... %>
  <% end %>
  <%= f.inputs :for => :profile do |profile| %>
    <%= profile.input :user ... %>
    <%= profile.input :categories ... %>
  <% end %>
<% end %>

Inputs for nested record (don't forget accepts_nested_attributes_for in your model, see Rails' fields_for documentation)

<% semantic_form_for @user do |form| %>
  <%= f.inputs do %>
    <%= f.input :name ... %>
    <%= f.input :email ... %>
  <% end %>
  <%= f.inputs :for => @user.profile do |profile| %>
    <%= profile.input :user ... %>
    <%= profile.input :categories ... %>
  <% end %>
<% end %>

Inputs for nested record with a different name (don't forget accepts_nested_attributes_for in your model, see Rails' fields_for documentation)

<% semantic_form_for @user do |form| %>
  <%= f.inputs do %>
    <%= f.input :name ... %>
    <%= f.input :email ... %>
  <% end %>
  <%= f.inputs :for => [:user_profile, @user.profile] do |profile| %>
    <%= profile.input :user ... %>
    <%= profile.input :categories ... %>
  <% end %>
<% end %>

Nesting #inputs blocks requires an extra <li> tag for valid markup

<% semantic_form_for @user do |form| %>
  <%= f.inputs do %>
    <%= f.input :name ... %>
    <%= f.input :email ... %>
    <li>
      <%= f.inputs :for => [:user_profile, @user.profile] do |profile| %>
        <%= profile.input :user ... %>
        <%= profile.input :categories ... %>
      <% end %>
    </li>
  <% end %>
<% end %>

Parameters:

  • *args (Hash)

    a customizable set of options

Options Hash (*args):

  • :for (Symbol, ActiveModel, Array)

    The contents of this option is passed down to Rails' fields_for() helper, so it accepts the same values.

  • :name (String)

    The optional name passed into the <legend> tag within the fieldset (alias of :title)

  • :title (String)

    The optional name passed into the <legend> tag within the fieldset (alias of :name)



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/formtastic/helpers/inputs_helper.rb', line 283

def inputs(*args, &block)
  wrap_it = @already_in_an_inputs_block ? true : false
  @already_in_an_inputs_block = true
  
  title = field_set_title_from_args(*args)
  html_options = args.extract_options!
  html_options[:class] ||= "inputs"
  html_options[:name] = title
  skipped_args = Array.wrap html_options.delete(:except)

  out = begin
    if html_options[:for] # Nested form
      inputs_for_nested_attributes(*(args << html_options), &block)
    elsif block_given?
      field_set_and_list_wrapping(*(args << html_options), &block)
    else
      legend = args.shift if args.first.is_a?(::String)
      args = default_columns_for_object - skipped_args if @object && args.empty?
      contents = fieldset_contents_from_column_list(args)
      args.unshift(legend) if legend.present?
      field_set_and_list_wrapping(*((args << html_options) << contents))
    end
  end
  
  out = template.(:li, out, :class => "input") if wrap_it
  @already_in_an_inputs_block = wrap_it
  out
end