Class: Formtastic::SemanticFormBuilder
- Inherits:
-
ActionView::Helpers::FormBuilder
- Object
- ActionView::Helpers::FormBuilder
- Formtastic::SemanticFormBuilder
- Defined in:
- lib/formtastic.rb
Constant Summary collapse
- RESERVED_COLUMNS =
[:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
- INLINE_ERROR_TYPES =
[:sentence, :list, :first]
Instance Attribute Summary collapse
-
#template ⇒ Object
Returns the value of attribute template.
Instance Method Summary collapse
-
#buttons(*args, &block) ⇒ Object
Creates a fieldset and ol tag wrapping for form buttons / actions as list items.
-
#commit_button(*args) ⇒ Object
Creates a submit input tag with the value “Save [model name]” (for existing records) or “Create [model name]” (for new records) by default:.
- #get_maxlength_for(method) ⇒ Object
-
#inline_errors_for(method, options = {}) ⇒ Object
(also: #errors_on)
Generates error messages for the given method.
-
#input(method, options = {}) ⇒ Object
Returns a suitable form input for the given
method, using the database column information and other factors (like the method name) to figure out what you probably want. -
#inputs(*args, &block) ⇒ Object
Creates an input fieldset and ol tag wrapping for use around a set of inputs.
-
#label(method, options_or_text = nil, options = nil) ⇒ Object
Generates the label for the input.
-
#semantic_errors(*args) ⇒ Object
Generates error messages for given method names and for base.
-
#semantic_fields_for(record_or_name_or_array, *args, &block) ⇒ Object
A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder for nesting forms:.
Instance Attribute Details
#template ⇒ Object
Returns the value of attribute template.
52 53 54 |
# File 'lib/formtastic.rb', line 52 def template @template end |
Instance Method Details
#buttons(*args, &block) ⇒ Object
Creates a fieldset and ol tag wrapping for form buttons / actions as list items. See inputs documentation for a full example. The fieldset’s default class attriute is set to “buttons”.
See inputs for html attributes and special options.
314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/formtastic.rb', line 314 def (*args, &block) = args. [:class] ||= "buttons" if block_given? field_set_and_list_wrapping(, &block) else args = [:commit] if args.empty? contents = args.map { || send(:"#{}_button") } field_set_and_list_wrapping(, contents) end end |
#commit_button(*args) ⇒ Object
Creates a submit input tag with the value “Save [model name]” (for existing records) or “Create [model name]” (for new records) by default:
<%= form.commit_button %> => <input name="commit" type="submit" value="Save Post" />
The value of the button text can be overridden:
<%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
<%= form.commit_button :label => "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
And you can pass html atributes down to the input, with or without the button text:
<%= form.commit_button :button_html => { :class => "pretty" } %> => <input name="commit" type="submit" value="Save Post" class="pretty {create|update|submit}" />
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/formtastic.rb', line 340 def (*args) = args. text = .delete(:label) || args.shift if @object && (@object.respond_to?(:persisted?) || @object.respond_to?(:new_record?)) if @object.respond_to?(:persisted?) # ActiveModel key = @object.persisted? ? :update : :create else # Rails 2 key = @object.new_record? ? :create : :update end # Deal with some complications with ActiveRecord::Base.human_name and two name models (eg UserPost) # ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost") # if there's no i18n, which is pretty crappy. In this circumstance we want to detect this # fall back (human_name == name.humanize) and do our own thing name.underscore.humanize ("User Post") if @object.class.model_name.respond_to?(:human) object_name = @object.class.model_name.human else object_human_name = @object.class.human_name # default is UserPost => "Userpost", but i18n may do better ("User post") crappy_human_name = @object.class.name.humanize # UserPost => "Userpost" decent_human_name = @object.class.name.underscore.humanize # UserPost => "User post" object_name = (object_human_name == crappy_human_name) ? decent_human_name : object_human_name end else key = :submit object_name = @object_name.to_s.send(label_str_method) end text = (localized_string(key, text, :action, :model => object_name) || ::Formtastic::I18n.t(key, :model => object_name)) unless text.is_a?(::String) = .delete(:button_html) || {} .merge!(:class => [[:class], key].compact.join(' ')) wrapper_html_class = ['commit'] # TODO: Add class reflecting on form action. wrapper_html = .delete(:wrapper_html) || {} wrapper_html[:class] = (wrapper_html_class << wrapper_html[:class]).flatten.compact.join(' ') accesskey = (.delete(:accesskey) || ) unless .has_key?(:accesskey) = .merge(:accesskey => accesskey) if accesskey template.content_tag(:li, Formtastic::Util.html_safe(submit(text, )), wrapper_html) end |
#get_maxlength_for(method) ⇒ Object
1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 |
# File 'lib/formtastic.rb', line 1680 def get_maxlength_for(method) validation = validations_for(method).find do |validation| (validation.respond_to?(:macro) && validation.macro == :validates_length_of) || # Rails 2 validation (validation.respond_to?(:kind) && validation.kind == :length) # Rails 3 validator end if validation validation.[:maximum] || (validation.[:within].present? ? validation.[:within].max : nil) else nil end end |
#inline_errors_for(method, options = {}) ⇒ Object Also known as: errors_on
Generates error messages for the given method. Errors can be shown as list, as sentence or just the first error can be displayed. If :none is set, no error is shown.
This method is also aliased as errors_on, so you can call on your custom inputs as well:
semantic_form_for :post do |f|
f.text_field(:body)
f.errors_on(:body)
end
462 463 464 465 466 467 468 469 |
# File 'lib/formtastic.rb', line 462 def inline_errors_for(method, = {}) #:nodoc: if render_inline_errors? errors = error_keys(method, ).map{|x| @object.errors[x] }.flatten.compact.uniq send(:"error_#{inline_errors}", [*errors], ) if errors.any? else nil end end |
#input(method, options = {}) ⇒ Object
Returns a suitable form input for the given method, using the database column information and other factors (like the method name) to figure out what you probably want.
Options:
-
:as - override the input type (eg force a :string to render as a :password field)
-
:label - use something other than the method name as the label text, when false no label is printed
-
:required - specify if the column is required (true) or not (false)
-
:hint - provide some text to hint or help the user provide the correct information for a field
-
:input_html - provide options that will be passed down to the generated input
-
:wrapper_html - provide options that will be passed down to the li wrapper
Input Types:
Most inputs map directly to one of ActiveRecord’s column types by default (eg string_input), but there are a few special cases and some simplification (:integer, :float and :decimal columns all map to a single numeric_input, for example).
-
:select (a select menu for associations) - default to association names
-
:check_boxes (a set of check_box inputs for associations) - alternative to :select has_many and has_and_belongs_to_many associations
-
:radio (a set of radio inputs for associations) - alternative to :select belongs_to associations
-
:time_zone (a select menu with time zones)
-
:password (a password input) - default for :string column types with ‘password’ in the method name
-
:text (a textarea) - default for :text column types
-
:date (a date select) - default for :date column types
-
:datetime (a date and time select) - default for :datetime and :timestamp column types
-
:time (a time select) - default for :time column types
-
:boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
-
:string (a text field) - default for :string column types
-
:numeric (a text field, like string) - default for :integer, :float and :decimal column types
-
:email (an email input) - default for :string column types with ‘email’ as the method name.
-
:url (a url input) - default for :string column types with ‘url’ as the method name.
-
:phone (a tel input) - default for :string column types with ‘phone’ or ‘fax’ in the method name.
-
:search (a search input) - default for :string column types with ‘search’ as the method name.
-
:country (a select menu of country names) - requires a country_select plugin to be installed
-
:email (an email input) - New in HTML5 - needs to be explicitly provided with :as => :email
-
:url (a url input) - New in HTML5 - needs to be explicitly provided with :as => :url
-
:phone (a tel input) - New in HTML5 - needs to be explicitly provided with :as => :phone
-
:search (a search input) - New in HTML5 - needs to be explicity provided with :as => :search
-
:country (a select menu of country names) - requires a country_select plugin to be installed
-
:hidden (a hidden field) - creates a hidden field (added for compatibility)
Example:
<% semantic_form_for @employee do |form| %>
<% form.inputs do -%>
<%= form.input :name, :label => "Full Name" %>
<%= form.input :manager, :as => :radio %>
<%= form.input :secret, :as => :password, :input_html => { :value => "xxxx" } %>
<%= form.input :hired_at, :as => :date, :label => "Date Hired" %>
<%= form.input :phone, :required => false, :hint => "Eg: +1 555 1234" %>
<%= form.input :email %>
<%= form.input :website, :as => :url, :hint => "You may wish to omit the http://" %>
<% end %>
<% end %>
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/formtastic.rb', line 110 def input(method, = {}) = .dup # Allow options to be shared without being tainted by Formtastic [:required] = method_required?(method) unless .key?(:required) [:as] ||= default_input_type(method, ) html_class = [ [:as], ([:required] ? :required : :optional) ] html_class << 'error' if has_errors?(method, ) wrapper_html = .delete(:wrapper_html) || {} wrapper_html[:id] ||= generate_html_id(method) wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ') if [:input_html] && [:input_html][:id] [:label_html] ||= {} [:label_html][:for] ||= [:input_html][:id] end input_parts = (custom_inline_order[[:as]] || inline_order).dup input_parts = input_parts - [:errors, :hints] if [:as] == :hidden list_item_content = input_parts.map do |type| send(:"inline_#{type}_for", method, ) end.compact.join("\n") return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html) end |
#inputs(*args, &block) ⇒ Object
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. These two examples are functionally equivalent:
# With a block:
<% semantic_form_for @post do |form| %>
<% form.inputs do %>
<%= form.input :title %>
<%= form.input :body %>
<% end %>
<% end %>
# With a list of fields:
<% semantic_form_for @post do |form| %>
<%= form.inputs :title, :body %>
<% end %>
# Output:
<form ...>
<fieldset class="inputs">
<ol>
<li class="string">...</li>
<li class="text">...</li>
</ol>
</fieldset>
</form>
Quick Forms
When called without a block or a field list, an input is rendered for each column in the model’s database table, just like Rails’ scaffolding. You’ll obviously 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| %>
<%= form.inputs %>
<% end %>
With a few arguments:
<% semantic_form_for @post do |form| %>
<%= form.inputs "Post details", :title, :body %>
<% end %>
Options
All options (with the exception of :name/:title) are passed down to the fieldset as HTML attributes (id, class, style, etc). If provided, the :name/:title option is passed into a legend tag inside the fieldset.
# With a block:
<% semantic_form_for @post do |form| %>
<% form.inputs :name => "Create a new post", :style => "border:1px;" do %>
...
<% end %>
<% end %>
# With a list (the options must come after the field list):
<% semantic_form_for @post do |form| %>
<%= form.inputs :title, :body, :name => "Create a new post", :style => "border:1px;" %>
<% end %>
# ...or the equivalent:
<% semantic_form_for @post do |form| %>
<%= form.inputs "Create a new post", :title, :body, :style => "border:1px;" %>
<% end %>
It’s basically a fieldset!
Instead of hard-coding fieldsets & legends into your form to logically group related fields, use inputs:
<% semantic_form_for @post do |f| %>
<% f.inputs do %>
<%= f.input :title %>
<%= f.input :body %>
<% end %>
<% f.inputs :name => "Advanced", :id => "advanced" do %>
<%= f.input :created_at %>
<%= f.input :user_id, :label => "Author" %>
<% end %>
<% f.inputs "Extra" do %>
<%= f.input :update_at %>
<% end %>
<% end %>
# Output:
<form ...>
<fieldset class="inputs">
<ol>
<li class="string">...</li>
<li class="text">...</li>
</ol>
</fieldset>
<fieldset class="inputs" id="advanced">
<legend><span>Advanced</span></legend>
<ol>
<li class="datetime">...</li>
<li class="select">...</li>
</ol>
</fieldset>
<fieldset class="inputs">
<legend><span>Extra</span></legend>
<ol>
<li class="datetime">...</li>
</ol>
</fieldset>
</form>
Nested attributes
As in Rails, you can use semantic_fields_for to nest attributes:
<% semantic_form_for @post do |form| %>
<%= form.inputs :title, :body %>
<% form.semantic_fields_for :author, @bob do |author_form| %>
<% author_form.inputs do %>
<%= author_form.input :first_name, :required => false %>
<%= author_form.input :last_name %>
<% end %>
<% end %>
<% end %>
But this does not look formtastic! This is equivalent:
<% semantic_form_for @post do |form| %>
<%= form.inputs :title, :body %>
<% form.inputs :for => [ :author, @bob ] do |author_form| %>
<%= author_form.input :first_name, :required => false %>
<%= author_form.input :last_name %>
<% end %>
<% end %>
And if you don’t need to give options to your input call, you could do it in just one line:
<% semantic_form_for @post do |form| %>
<%= form.inputs :title, :body %>
<%= form.inputs :first_name, :last_name, :for => @bob %>
<% end %>
Just remember that calling inputs generates a new fieldset to wrap your inputs. If you have two separate models, but, semantically, on the page they are part of the same fieldset, you should use semantic_fields_for instead (just as you would do with Rails’ form builder).
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/formtastic.rb', line 284 def inputs(*args, &block) title = field_set_title_from_args(*args) = args. [:class] ||= "inputs" [:name] = title if [:for] # Nested form inputs_for_nested_attributes(*(args << ), &block) elsif block_given? field_set_and_list_wrapping(*(args << ), &block) else if @object && args.empty? args = association_columns(:belongs_to) args += content_columns args -= RESERVED_COLUMNS args.compact! end legend = args.shift if args.first.is_a?(::String) contents = args.collect { |method| input(method.to_sym) } args.unshift(legend) if legend.present? field_set_and_list_wrapping(*((args << ) << contents)) end end |
#label(method, options_or_text = nil, options = nil) ⇒ Object
Generates the label for the input. It also accepts the same arguments as Rails label method. It has three options that are not supported by Rails label method:
-
:required - Appends an abbr tag if :required is true
-
:label - An alternative form to give the label content. Whenever label
is false, a blank string is returned. -
:input_name - Gives the input to match for. This is needed when you want to
to call f.label :authors but it should match :author_ids.
Examples
f.label :title # like in rails, except that it searches the label on I18n API too
f.label :title, "Your post title"
f.label :title, :label => "Your post title" # Added for formtastic API
f.label :title, :required => true # Returns <label>Title<abbr title="required">*</abbr></label>
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/formtastic.rb', line 428 def label(method, =nil, =nil) if .is_a?(Hash) return "" if [:label] == false = text = .delete(:label) else text = ||= {} end text = localized_string(method, text, :label) || humanized_attribute_name(method) text += required_or_optional_string(.delete(:required)) text = Formtastic::Util.html_safe(text) # special case for boolean (checkbox) labels, which have a nested input if .key?(:label_prefix_for_nested_input) text = .delete(:label_prefix_for_nested_input) + text end input_name = .delete(:input_name) || method super(input_name, text, ) end |
#semantic_errors(*args) ⇒ Object
Generates error messages for given method names and for base. You can pass a hash with html options that will be added to ul tag
Examples
f.semantic_errors # This will show only errors on base
f.semantic_errors :state # This will show errors on base and state
f.semantic_errors :state, :class => "awesome" # errors will be rendered in ul.awesome
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
# File 'lib/formtastic.rb', line 481 def semantic_errors(*args) = args. full_errors = args.inject([]) do |array, method| attribute = localized_string(method, method.to_sym, :label) || humanized_attribute_name(method) errors = Array(@object.errors[method.to_sym]).to_sentence errors.present? ? array << [attribute, errors].join(" ") : array ||= [] end full_errors << @object.errors[:base] full_errors.flatten! full_errors.compact! return nil if full_errors.blank? [:class] ||= "errors" template.content_tag(:ul, ) do Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join) end end |
#semantic_fields_for(record_or_name_or_array, *args, &block) ⇒ Object
A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder for nesting forms:
# Example:
<% semantic_form_for @post do |post| %>
<% post.semantic_fields_for :author do |author| %>
<% author.inputs :name %>
<% end %>
<% end %>
# Output:
<form ...>
<fieldset class="inputs">
<ol>
<li class="string"><input type='text' name='post[author][name]' id='post_author_name' /></li>
</ol>
</fieldset>
</form>
402 403 404 405 406 407 |
# File 'lib/formtastic.rb', line 402 def semantic_fields_for(record_or_name_or_array, *args, &block) opts = args. opts[:builder] ||= self.class args.push(opts) fields_for(record_or_name_or_array, *args, &block) end |