Skip to content

Latest commit

 

History

History
173 lines (120 loc) · 7.37 KB

RENDERER.textile

File metadata and controls

173 lines (120 loc) · 7.37 KB

Formtastic Renderer

Replaceable render engines for Formtastic.

One of the things I love about Formtastic is the semantic DSL for specifying a form in my code. One of the things that can frustrate me is the resulting DOM that is generated. While you can often use CSS to work through the DOM structure and get the form to look how you want, it’s not always the way I would have done it, and there are cases that just aren’t possible. So I decided to refactor Formtastic. At first I looked at just subclassing the builder but decided to fork the project. I made some decisions where to draw the line between DSL and “render”, and split the rendering into a separate module.

More in blog post http://www.vaporbase.com/postings/Replaceable_render_engines_for_Formtastic

Renderer API

  render_input( input )

    input       hash may include keys
    
      :method         => symbol
      :as             => the input type (eg :string, :text, etc)
      :options        => original options
      :label          => string with html label tag
      :hint           => hints string
      :errors         => actual errors on the method
      :inline_errors  => string with inline error messages (pre-rendered per @@inline_errors setting)
      :wrapper        => hash of html attributes for the wrapper container (eg :class)
      :structure      => name of DOM structure (e.g. :inline) (or nil)
      :hidden         => string of additional hidden input tags

      WILL CONTAIN ONLY ONE OF THE FOLLOWING:
      
      :input          => string with html input tag (and attributes, children)

      :items          => array of input items to be rendered, each hash with keys
      
                          :input     => string with html input tag (eg radio)
                          :label     => string with label text (eg "Choice A")
                          :input_id  => string with target dom id of the input
                          :wrapper   => hash of html attributes for the wrapper container (eg :class)

      :chronos        => array of input items to be rendered, each hash with keys
      
                          :input     => string with html input tag
                          :label     => string with html label tag

Strings passed to the render API will have already been localized (I18n).

input will include only one of the keys :input, :items, or :chronos.

I decided to leave the inline error tags that Formtastic generates, which are based on the #inline_errors configuration option, since they make sense to me. If you really want to customize the error messages, the actual object errors are provided in :errors.

I am also leaving how Formtastic renders required labels (with attr tag). If your custom renderer need to know a field is required, look for options[:required] value.

    render_field_set( fieldset )
    
      :options        => original options
      :legend         => string that should go into the legend
      :contents       => string or array of html tag strings that make the content of the field set
      :wrapper        => hash of html attributes for the fieldset 
      :structure      => name of DOM structure (e.g. :inline) (or nil)

Configuration

You can replace the default plugin renderer by specifying the module here. Defaults to ClassicFormtasticRenderer.
Other possibilities include InspectorRenderer (for debugging) and ErbRenderer, or your own.


Formtastic::SemanticFormBuilder.renderer = Formtastic::ClassicFormtasticRenderer

New options

semantic_form_for :renderer => RENDERER

You can also specify the renderer on a case by case basis using the semantic_form_for :renderer option, overriding the configuration setting. The value is a symbol representing the module. For example, :renderer => :erb would load the Formtastic::ErbRenderer module, while :renderer => :classic_formtastic would load Formtastic::ClassicFormtasticRenderer

form.inputs :structure => DOM_STRUCTURE

Name of a DOM structure format, used by the renderer. I’m thinking we could have as set of standard built-in structures that would be supported by any renderer. (Presently only implemented by the ERB renderer. See below.)


:inline inputs group is wrapped in a div tag, and the items are in p tags

This is a quick easy way to choose an alternative DOM structure that’s generated for a set of fields. For example,

  <% form.inputs 'Full Name', :structure => :inline do %>
    <%= form.input :first_name,  :label => 'First',  :input_html => {:size => '15'} %>
    <%= form.input :middle_name, :label => 'Middle', :input_html => {:size => '5'} %>
    <%= form.input :last_name,   :label => 'Last',   :input_html => {:size => '15'} %>    
  <% end %>

would generate

  <li class="inputs" name="Full Name">
    <label>Full Name</label>
    <div>
      <p class="string required" id="user_first_name_input">
        <label for="user_first_name">First<abbr title="required">*</abbr></label><br>
        <input id="user_first_name" name="user[first_name]" size="15" type="text"><br>
      </p>
      <p class="string required" id="user_middle_name_input">
        <label for="user_middle_name">Middle<abbr title="required">*</abbr></label><br>
        <input id="user_middle_name" name="user[middle_name]" size="5" type="text"><br>
      </p>
      <p class="string required" id="user_last_name_input">
        <label for="user_last_name">Last<abbr title="required">*</abbr></label><br>
        <input id="user_last_name" name="user[last_name]" size="15" type="text"><br>
      </p>
    </div>
  </li>

Renderers

ClassicFormtasticRenderer

Implements the same behavior as the original Formtastic. Using this renderer, the original Formtastic specs pass.

ErbRenderer

The ERB renderer renders an ERB partial for each input. There are 4 built-in partials that emulate the classic Formtastic rendering.

  _input.html.erb             general input
  _items.html.erb             radio and check box items
  _chronos.html.erb           date and time selects
  _fields.html.erb            field set wrapper

You can create a partial for each and any specific input (the :as value), such as “_text.html.erb” for a text area. If not found, it then looks for a partial for the input category (“chronos” for date and time selects; “items” for radio and check boxes’ and “input” for all others).

The renderer looks for partials in the current controller’s view directory (eg app/views/foos/text.html.erb), then in a Formatastic view directory (app/views/formtastic/text.html.erb), and then in the plug-in’s directory.

Using the ERB renderer, form.input takes the following new options:

  :partial     => "partial"    name of partial to use (overrides the defaults)

form.inputs take the following new options:

  :partial     => "partial"    name of partial to use (overrides the default "fields")

The ERB renderer uses the :structure option as a prefix on partial names. Thus, doing

  <% form.fields :structure => :foo do %>
    <%= form.input :name %>
    ...

is equivalent to

  <% form.fields :partial => 'foo_fields' do %>
    <%= form.input :name, :partial => 'foo_input' %>
    ...

InspectorRenderer

Dumps the arguments to the renderer api (in yaml format). Useful for debugging.

To Do

  • erb: option to specify inline template string in the DSL