Git Product home page Git Product logo

formtastic's Introduction

Formtastic

Build Status Inline docs Code Climate Gem Version

Formtastic is a Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.

Documentation & Support

Compatibility

  • Formtastic 5 requires Rails 6.0 and Ruby 2.6 minimum
  • Formtastic 4 requires Rails 5.2 and Ruby 2.4 minimum
  • Formtastic 3 requires Rails 3.2.13 minimum
  • Formtastic 2 requires Rails 3
  • Formtastic, much like Rails, is very ActiveRecord-centric. Many are successfully using other ActiveModel-like ORMs and objects (DataMapper, MongoMapper, Mongoid, Authlogic, Devise...) but we're not guaranteeing full compatibility at this stage. Patches are welcome!

The Story

One day, I finally had enough, so I opened up my text editor, and wrote a DSL for how I'd like to author forms:

  <%= semantic_form_for @article do |f| %>

    <%= f.inputs :name => "Basic" do %>
      <%= f.input :title %>
      <%= f.input :body %>
      <%= f.input :section %>
      <%= f.input :publication_state, :as => :radio %>
      <%= f.input :category %>
      <%= f.input :allow_comments, :label => "Allow commenting on this article" %>
    <% end %>

    <%= f.inputs :name => "Advanced" do %>
      <%= f.input :keywords, :required => false, :hint => "Example: ruby, rails, forms" %>
      <%= f.input :extract, :required => false %>
      <%= f.input :description, :required => false %>
      <%= f.input :url_title, :required => false %>
    <% end %>

    <%= f.inputs :name => "Author", :for => :author do |author_form| %>
      <%= author_form.input :first_name %>
      <%= author_form.input :last_name %>
    <% end %>

    <%= f.actions do %>
      <%= f.action :submit, :as => :button %>
      <%= f.action :cancel, :as => :link %>
    <% end %>

  <% end %>

I also wrote the accompanying HTML output I expected, favoring something very similar to the fieldsets, lists and other semantic elements Aaron Gustafson presented in Learning to Love Forms, hacking together enough Ruby to prove it could be done.

It's awesome because...

  • It can handle belongs_to associations (like Post belongs_to :author), rendering a select or set of radio inputs with choices from the parent model.
  • It can handle has_many and has_and_belongs_to_many associations (like: Post has_many :tags), rendering a multi-select with choices from the child models.
  • It's Rails 3/4 compatible (including nested forms).
  • It has internationalization (I18n)!
  • It's really quick to get started with a basic form in place (4 lines), then go back to add in more detail if you need it.
  • There's heaps of elements, id and class attributes for you to hook in your CSS and JS.
  • It handles real world stuff like inline hints, inline error messages & help text.
  • It doesn't hijack or change any of the standard Rails form inputs, so you can still use them as expected (even mix and match).
  • It's got absolutely awesome spec coverage.
  • There's a bunch of people using and working on it (it's not just one developer building half a solution).
  • It has growing HTML5 support (new inputs like email/phone/search, new attributes like required/min/max/step/placeholder)

Opinions

  • It should be easier to do things the right way than the wrong way.
  • Sometimes more mark-up is better.
  • Elements and attribute hooks are gold for stylesheet authors.
  • Make the common things we do easy, yet ensure uncommon things are still possible.

Installation

Simply add Formtastic to your Gemfile and bundle it up:

  gem 'formtastic', '~> 5.0'

Run the installation generator:

$ rails generate formtastic:install

Stylesheets

A proof-of-concept set of stylesheets are provided which you can include in your layout. Customization is best achieved by overriding these styles in an additional stylesheet.

Rails 3.1 introduces an asset pipeline that allows plugins like Formtastic to serve their own Stylesheets, Javascripts, etc without having to run generators that copy them across to the host application. Formtastic makes three stylesheets available as an Engine, you just need to require them in your global stylesheets.

  # app/assets/stylesheets/application.css
  *= require formtastic
  *= require my_formtastic_changes

Conditional stylesheets need to be compiled separately to prevent them being bundled and included with other application styles. Remove require_tree . from application.css and specify required stylesheets individually.

  # app/assets/stylesheets/ie6.css
  *= require formtastic_ie6

  # app/assets/stylesheets/ie7.css
  *= require formtastic_ie7
  # app/views/layouts/application.html.erb
  <%= stylesheet_link_tag 'application' %>
  <!--[if IE 6]><%= stylesheet_link_tag 'ie6' %><![endif]-->
  <!--[if IE 7]><%= stylesheet_link_tag 'ie7' %><![endif]-->
  # config/environments/production.rb
  config.assets.precompile += %w( ie6.css ie7.css )

Usage

Forms are really boring to code... you want to get onto the good stuff as fast as possible.

This renders a set of inputs (one for most columns in the database table, and one for each ActiveRecord belongs_to-association), followed by default action buttons (an input submit button):

  <%= semantic_form_for @user do |f| %>
    <%= f.inputs %>
    <%= f.actions %>
  <% end %>

This is a great way to get something up fast, but like scaffolding, it's not recommended for production. Don't be so lazy!

To specify the order of the fields, skip some of the fields or even add in fields that Formtastic couldn't infer. You can pass in a list of field names to inputs and list of action names to actions:

  <%= semantic_form_for @user do |f| %>
    <%= f.inputs :title, :body, :section, :categories, :created_at %>
    <%= f.actions :submit, :cancel %>
  <% end %>

You probably want control over the input type Formtastic uses for each field. You can expand the inputs and actions to block helper format and use the :as option to specify an exact input type:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title %>
      <%= f.input :body %>
      <%= f.input :section, :as => :radio %>
      <%= f.input :categories %>
      <%= f.input :created_at, :as => :string %>
    <% end %>
    <%= f.actions do %>
      <%= f.action :submit, :as => :button %>
      <%= f.action :cancel, :as => :link %>
    <% end %>
  <% end %>

If you want to customize the label text, or render some hint text below the field, specify which fields are required/optional, or break the form into two fieldsets, the DSL is pretty comprehensive:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs "Basic", :id => "basic" do %>
      <%= f.input :title %>
      <%= f.input :body %>
    <% end %>
    <%= f.inputs :name => "Advanced Options", :id => "advanced" do %>
      <%= f.input :slug, :label => "URL Title", :hint => "Created automatically if left blank", :required => false %>
      <%= f.input :section, :as => :radio %>
      <%= f.input :user, :label => "Author" %>
      <%= f.input :categories, :required => false %>
      <%= f.input :created_at, :as => :string, :label => "Publication Date", :required => false %>
    <% end %>
    <%= f.actions do %>
      <%= f.action :submit %>
    <% end %>
  <% end %>

You can create forms for nested resources:

	<%= semantic_form_for [@author, @post] do |f| %>

Nested forms are also supported (don't forget your models need to be setup correctly with accepts_nested_attributes_for). You can do it in the Rails way:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs :title, :body, :created_at %>
    <%= f.semantic_fields_for :author do |author| %>
      <%= author.inputs :first_name, :last_name, :name => "Author" %>
    <% end %>
    <%= f.actions %>
  <% end %>

Or the Formtastic way with the :for option:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs :title, :body, :created_at %>
    <%= f.inputs :first_name, :last_name, :for => :author, :name => "Author" %>
    <%= f.actions %>
  <% end %>

When working in has many association, you can even supply "%i" in your fieldset name; they will be properly interpolated with the child index. For example:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs %>
    <%= f.inputs :name => 'Category #%i', :for => :categories %>
    <%= f.actions %>
  <% end %>

Alternatively, the current index can be accessed via the inputs block's arguments for use anywhere:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs :for => :categories do |category, i| %>
      ...
    <%= f.actions %>
  <% end %>

If you have more than one form on the same page, it may lead to HTML invalidation because of the way HTML element id attributes are assigned. You can provide a namespace for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generate HTML id. For example:

  <%= semantic_form_for(@post, :namespace => 'cat_form') do |f| %>
    <%= f.inputs do %>
      <%= f.input :title %>        # id="cat_form_post_title"
      <%= f.input :body %>         # id="cat_form_post_body"
      <%= f.input :created_at %>   # id="cat_form_post_created_at"
    <% end %>
    <%= f.actions %>
  <% end %>

Customize HTML attributes for any input using the :input_html option. Typically this is used to disable the input, change the size of a text field, change the rows in a textarea, or even to add a special class to an input to attach special behavior like autogrow textareas:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title,      :input_html => { :size => 10 } %>
      <%= f.input :body,       :input_html => { :class => 'autogrow', :rows => 10, :cols => 20, :maxlength => 10  } %>
      <%= f.input :created_at, :input_html => { :disabled => true } %>
      <%= f.input :updated_at, :input_html => { :readonly => true } %>
    <% end %>
    <%= f.actions %>
  <% end %>

The same can be done for actions with the :button_html option:

  <%= semantic_form_for @post do |f| %>
    ...
    <%= f.actions do %>
      <%= f.action :submit, :button_html => { :class => "primary", :disable_with => 'Wait...' } %>
    <% end %>
  <% end %>

Customize the HTML attributes for the <li> wrapper around every input with the :wrapper_html option hash. There's one special key in the hash: (:class), which will actually append your string of classes to the existing classes provided by Formtastic (like "required string error").

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title, :wrapper_html => { :class => "important" } %>
      <%= f.input :body %>
      <%= f.input :description, :wrapper_html => { :style => "display:none;" } %>
    <% end %>
    ...
  <% end %>

Many inputs provide a collection of options to choose from (like :select, :radio, :check_boxes, :boolean). In many cases, Formtastic can find choices through the model associations, but if you want to use your own set of choices, the :collection option is what you want. You can pass in an Array of objects, an array of Strings, a Hash... Throw almost anything at it! Examples:

  f.input :authors, :as => :check_boxes, :collection => User.order("last_name ASC").all
  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.all
  f.input :author,  :as => :select,      :collection => Author.pluck(:first_name, :id)
  f.input :author,  :as => :select,      :collection => Author.pluck(Arel.sql("CONCAT(`first_name`, ' ', `last_name`)"), :id)
  f.input :author,  :as => :select,      :collection => Author.your_custom_scope_or_class_method
  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.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"]
  f.input :book_id, :as => :select,      :collection => Hash[Book.all.map{|b| [b.name,b.id]}]
  f.input :fav_book,:as => :datalist   , :collection => Book.pluck(:name)

The Available Inputs

The Formtastic input types:

  • :select - a select menu. Default for ActiveRecord associations: belongs_to, has_many, and has_and_belongs_to_many.
  • :check_boxes - a set of check_box inputs. Alternative to :select for ActiveRecord-associations: has_many, and has_and_belongs_to_many`.
  • :radio - a set of radio inputs. Alternative to :select for ActiveRecord-associations: belongs_to.
  • :time_zone - a select input. Default for column types: :string with name matching "time_zone".
  • :password - a password input. Default for column types: :string with name matching "password".
  • :text - a textarea. Default for column types: :text.
  • :date_select - a date select. Default for column types: :date.
  • :datetime_select - a date and time select. Default for column types: :datetime and :timestamp.
  • :time_select - a time select. Default for column types: :time.
  • :boolean - a checkbox. Default for column types: :boolean.
  • :string - a text field. Default for column types: :string.
  • :number - a text field (just like string). Default for column types: :integer, :float, and :decimal.
  • :file - a file field. Default for file-attachment attributes matching: paperclip or attachment_fu.
  • :country - a select menu of country names. Default for column types: :string with name "country" - requires a country_select plugin to be installed.
  • :email - a text field (just like string). Default for columns with name matching "email". New in HTML5. Works on some mobile browsers already.
  • :url - a text field (just like string). Default for columns with name matching "url". New in HTML5. Works on some mobile browsers already.
  • :phone - a text field (just like string). Default for columns with name matching "phone" or "fax". New in HTML5.
  • :search - a text field (just like string). Default for columns with name matching "search". New in HTML5. Works on Safari.
  • :hidden - a hidden field. Creates a hidden field (added for compatibility).
  • :range - a slider field.
  • :datalist - a text field with a accompanying datalist tag which provides options for autocompletion

The comments in the code are pretty good for each of these (what it does, what the output is, what the options are, etc.) so go check it out.

Delegation for label lookups

Formtastic decides which label to use in the following order:

  1. :label             # :label => "Choose Title"
  2. Formtastic i18n    # if either :label => true || i18n_lookups_by_default = true (see Internationalization)
  3. Activerecord i18n  # if localization file found for the given attribute
  4. label_str_method   # if nothing provided this defaults to :humanize but can be set to a custom method

Internationalization (I18n)

Basic Localization

Formtastic has some neat I18n-features. ActiveRecord object names and attributes are, by default, taken from calling @object.human_name and @object.human_attribute_name(attr) respectively. There are a few words specific to Formtastic that can be translated. See lib/locale/en.yml for more information.

Basic localization (labels only, with ActiveRecord):

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title %>        # => :label => I18n.t('activerecord.attributes.user.title')    or 'Title'
      <%= f.input :body %>         # => :label => I18n.t('activerecord.attributes.user.body')     or 'Body'
      <%= f.input :section %>      # => :label => I18n.t('activerecord.attributes.user.section')  or 'Section'
    <% end %>
  <% end %>

Note: This is perfectly fine if you just want your labels/attributes and/or models to be translated using ActiveRecord I18n attribute translations, and you don't use input hints and legends. But what if you do? And what if you don't want same labels in all forms?

Enhanced Localization (Formtastic I18n API)

Formtastic supports localized labels, hints, legends, actions using the I18n API for more advanced usage. Your forms can now be DRYer and more flexible than ever, and still fully localized. This is how:

1. Enable I18n lookups by default (config/initializers/formtastic.rb):

  Formtastic::FormBuilder.i18n_lookups_by_default = true

2. Add some label-translations/variants (config/locales/en.yml):

  en:
    formtastic:
      titles:
        post_details: "Post details"
      labels:
        post:
          title: "Your Title"
          body: "Write something..."
          edit:
            title: "Edit title"
      hints:
        post:
          title: "Choose a good title for your post."
          body: "Write something inspiring here."
      placeholders:
        post:
          title: "Title your post"
          slug: "Leave blank for an automatically generated slug"
        user:
          email: "[email protected]"
      actions:
        create: "Create my %{model}"
        update: "Save changes"
        reset: "Reset form"
        cancel: "Cancel and go back"
        dummie: "Launch!"

3. ...and now you'll get:

  <%= semantic_form_for Post.new do |f| %>
    <%= f.inputs do %>
      <%= f.input :title %>      # => :label => "Choose a title...", :hint => "Choose a good title for your post."
      <%= f.input :body %>       # => :label => "Write something...", :hint => "Write something inspiring here."
      <%= f.input :section %>    # => :label => I18n.t('activerecord.attributes.user.section')  or 'Section'
    <% end %>
    <%= f.actions do %>
      <%= f.action :submit %>   # => "Create my %{model}"
    <% end %>
  <% end %>

4. Localized titles (a.k.a. legends):

Note: Slightly different because Formtastic can't guess how you group fields in a form. Legend text can be set with first (as in the sample below) specified value, or :name/:title options - depending on what flavor is preferred.

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs :post_details do %>   # => :title => "Post details"
      # ...
    <% end %>
    # ...
<% end %>

5. Override I18n settings:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title %>      # => :label => "Choose a title...", :hint => "Choose a good title for your post."
      <%= f.input :body, :hint => false %>                 # => :label => "Write something..."
      <%= f.input :section, :label => 'Some section' %>    # => :label => 'Some section'
    <% end %>
    <%= f.actions do %>
      <%= f.action :submit, :label => :dummie %>         # => "Launch!"
    <% end %>
  <% end %>

If I18n-lookups is disabled, i.e.:

  Formtastic::FormBuilder.i18n_lookups_by_default = false

...then you can enable I18n within the forms instead:

  <%= semantic_form_for @post do |f| %>
    <%= f.inputs do %>
      <%= f.input :title, :label => true %>      # => :label => "Choose a title..."
      <%= f.input :body, :label => true %>       # => :label => "Write something..."
      <%= f.input :section, :label => true %>    # => :label => I18n.t('activerecord.attributes.user.section')  or 'Section'
    <% end %>
    <%= f.actions do %>
      <%= f.action :submit, :label => true %>    # => "Update %{model}" (if we are in edit that is...)
    <% end %>
  <% end %>

6. Advanced I18n lookups

For more flexible forms; Formtastic finds translations using a bottom-up approach taking the following variables in account:

  • MODEL, e.g. "post"
  • ACTION, e.g. "edit"
  • KEY/ATTRIBUTE, e.g. "title", :my_custom_key, ...

...in the following order:

  1. formtastic.{titles,labels,hints,actions}.MODEL.ACTION.ATTRIBUTE - by model and action
  2. formtastic.{titles,labels,hints,actions}.MODEL.ATTRIBUTE - by model
  3. formtastic.{titles,labels,hints,actions}.ATTRIBUTE - global default

...which means that you can define translations like this:

  en:
    formtastic:
      labels:
        title: "Title"  # Default global value
        article:
          body: "Article content"
        post:
          new:
            title: "Choose a title..."
            body: "Write something..."
          edit:
            title: "Edit title"
            body: "Edit body"

Values for labels/hints/actions are can take values: String (explicit value), Symbol (i18n-lookup-key relative to the current "type", e.g. actions:), true (force I18n lookup), false (force no I18n lookup). Titles (legends) can only take: String and Symbol - true/false have no meaning.

7. Basic Translations If you want some basic translations, take a look on the formtastic_i18n gem.

Semantic errors

You can show errors on base (by default) and any other attribute just by passing its name to the semantic_errors method:

  <%= semantic_form_for @post do |f| %>
    <%= f.semantic_errors :state %>
  <% end %>

Modified & Custom Inputs

You can modify existing inputs, subclass them, or create your own from scratch. Here's the basic process:

  • Run the input generator and provide your custom input name. For example, rails generate formtastic:input hat_size. This creates the file app/inputs/hat_size_input.rb. You can also provide namespace to input name like rails generate formtastic:input foo/custom or rails generate formtastic:input Foo::Custom, this will create the file app/inputs/foo/custom_input.rb in both cases.
  • To use that input, leave off the word "input" in your as statement. For example, f.input(:size, :as => :hat_size)

Specific examples follow.

Changing Existing Input Behavior

To modify the behavior of StringInput, subclass it in a new file, app/inputs/string_input.rb:

  class StringInput < Formtastic::Inputs::StringInput
    def to_html
      puts "this is my modified version of StringInput"
      super
    end
  end

Another way to modify behavior is by using the input generator:

$ rails generate formtastic:input string --extend

This generates the file app/inputs/string_input.rb with its respective content class.

You can use your modified version with :as => :string.

Creating New Inputs Based on Existing Ones

To create your own new types of inputs based on existing inputs, the process is similar. For example, to create FlexibleTextInput based on StringInput, put the following in app/inputs/flexible_text_input.rb:

  class FlexibleTextInput < Formtastic::Inputs::StringInput
    def input_html_options
      super.merge(:class => "flexible-text-area")
    end

    def options
      super.merge(hint: 'This is a flexible text area')
    end
  end

You can also extend existing input behavior by using the input generator:

$ rails generate formtastic:input FlexibleText --extend string

This generates the file app/inputs/flexible_text_input.rb with its respective content class.

You can use your new input with :as => :flexible_text.

Creating New Inputs From Scratch

To create a custom DatePickerInput from scratch, put the following in app/inputs/date_picker_input.rb:

  class DatePickerInput
    include Formtastic::Inputs::Base
    def to_html
      # ...
    end
  end

You can use your new input with :as => :date_picker.

Dependencies

There are none other than Rails itself, but...

  • If you want to use the :country input, you'll need to install the country-select plugin (or any other country_select plugin with the same API). Both 1.x and 2.x are supported, but they behave differently when storing data, so please see their upgrade notes if switching from 1.x.
  • There are a bunch of development dependencies if you plan to contribute to Formtastic

How to contribute

  • Fork the project on Github
  • Install development dependencies (bundle install and bin/appraisal install)
  • Create a topic branch for your changes
  • Ensure that you provide documentation and test coverage for your changes (patches won't be accepted without)
  • Ensure that all tests pass (bundle exec rake)
  • Create a pull request on Github (these are also a great place to start a conversation around a patch as early as possible)

Project Info

Formtastic was created by Justin French with contributions from around 180 awesome developers. Run git shortlog -n -s to see the awesome.

The project is hosted on Github: https://github.com/formtastic/formtastic, where your contributions, forkings, comments, issues and feedback are greatly welcomed.

Copyright (c) 2007-2021, released under the MIT license.

formtastic's People

Contributors

adzap avatar andypearson avatar danielricecodes avatar deivid-rodriguez avatar dwbutler avatar ebeigarts avatar farnoy avatar grimen avatar haines avatar josevalim avatar justinfrench avatar ktaragorn avatar lardawge avatar lostapathy avatar lsylvester avatar markmansour avatar mhuggins avatar mikz avatar mjonuschat avatar negonicrac avatar pat avatar pfeiffer avatar pranas avatar sobrinho avatar sprsquish avatar st0012 avatar tiendung avatar timoschilling avatar twalpole avatar vjt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

formtastic's Issues

OrderedHash as collection not recognized as Hash

For hard coded options for select fields I like to go with hashes since they map very nicely to the select option concept (key -> values). But since the Ruby 1.8 hash is not ordered I always use OrderedHash instead (sudo gem install orderedhash).

Formtastic didn't recognize it as a Hash at line 1075 of formtastic.rb:
collection = collection.to_a if collection.instance_of?(Hash)

I did a simple local hack and changed instance_of? to is_a?:
collection = collection.to_a if collection.is_a?(Hash)

So now the OrderedHash'es are working just fine as hashes. I didn't run any test suites against it or anything. If you agree it's a good idea I hope to see it in master.

Singularize / Pluralize problem in formtastic breaking on status/statuses.

As per a request by Justin in this thread: http://groups.google.com.au/group/formtastic/t/cfa36ee67d8345b I am creating this issue. This is in rails 2.3.2.

The problem appears to be formtastic somehow coming up with job_statu_id insteda of job_status_id when building.

First up the stacktrace:

ActionView::TemplateError: undefined method `job_statu_id' for #<Job:
0xb6718e6c>
On line #2 of app/views/jobs/_form.html.erb

    1: <% semantic_form_for @job do |form| %>
    2:   <%= form.inputs %>
    3:   <%= render :partial => "shared/formsubmitbutton" %>
    4: <% end %>

    vendor/plugins/formtastic/rails//lib/formtastic.rb:558:in

select_input' vendor/plugins/formtastic/rails//lib/formtastic.rb:884:in send'
vendor/plugins/formtastic/rails//lib/formtastic.rb:884:in
inline_input_for' vendor/plugins/formtastic/rails//lib/formtastic.rb:99:in send'
vendor/plugins/formtastic/rails//lib/formtastic.rb:99:in
input' vendor/plugins/formtastic/rails//lib/formtastic.rb:98:inmap'
vendor/plugins/formtastic/rails//lib/formtastic.rb:98:in
input' vendor/plugins/formtastic/rails//lib/formtastic.rb:247:in inputs'
vendor/plugins/formtastic/rails//lib/formtastic.rb:247:in
map' vendor/plugins/formtastic/rails//lib/formtastic.rb:247:in inputs'
app/views/jobs/_form.html.erb:2
vendor/plugins/formtastic/rails//lib/formtastic.rb:1225:in
semantic_form_for' app/views/jobs/_form.html.erb:1 app/views/jobs/new.html.erb:3 app/controllers/jobs_controller.rb:29:innew'
/test/functional/jobs_controller_test.rb:11:in
test_should_get_new' /usr/lib/ruby/1.8/test/unit/testsuite.rb:34:inrun'
/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in each' /usr/lib/ruby/1.8/test/unit/testsuite.rb:33:inrun'
/usr/lib/ruby/1.8/test/unit/testsuite.rb:34:in run' /usr/lib/ruby/1.8/test/unit/testsuite.rb:33:ineach'
/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in run' /usr/lib/ruby/1.8/test/unit/ui/testrunnermediator.rb:46:in old_run_suite'
/opt/rubymine/rb/testing/patch/test/unit/ui/
testrunnermediator.rb:36:in run_suite' /opt/rubymine/rb/testing/patch/test/unit/ui/teamcity/ testrunner.rb:69:instart_mediator'
/opt/rubymine/rb/testing/patch/test/unit/ui/teamcity/
testrunner.rb:57:in start' /usr/lib/ruby/1.8/test/unit/ui/testrunnerutilities.rb:29:in run'
/usr/lib/ruby/1.8/test/unit/autorunner.rb:216:in run' /usr/lib/ruby/1.8/test/unit/autorunner.rb:12:inrun'
/usr/lib/ruby/1.8/test/unit.rb:278
rake (0.8.4) lib/rake/rake_test_loader.rb:5

Next up we have the schema for the two tables:

create_table "job_statuses", :force => true do |t|
  t.integer "lock_version", :default => 0
  t.string "name", :limit => 50, :null => false

  t.timestamps
end


create_table "jobs", :force => true do |t|
  t.integer "lock_version", :default => 0
  t.references :customer, :null => false
  t.references :employee, :null => false
  t.datetime "job_date", :null => false
  t.text "description", :null => false
  t.references :job_status, :null => false
  t.decimal "estimated_time", :precision => 10, :scale => 2, :default => 0.00, :null => false

  t.timestamps
end

Associations:
in the Job model: belongs_to :job_status
in the JobStatus model: has_many :jobs

The view code:
in views/jobs/edit.html.erb:
<%= render :partial => "form" %>

in views/jobs/_form.html.erb

<% semantic_form_for @job do |form| %>
<%= form.inputs %>
<%= render :partial => "shared/formsubmitbutton" %>
<% end %>

the ability to provide :prefix and :suffix strings

Here's some real-world examples I've found recently:

[input] mt

[input] square meters

AU$ [input] excl. GST

These can all be "solved" with some help/hint text below the input or a verbose label, but I think we should try to figure out a better solution.

Wrapper around country select

A new input :as => :country should be added, which means we either require one of the country select plugins, or simply support any plugin that defines country_select as a form helper method.

formtastic paperclip attribute detection generates wrong html

Html generated with autodetection:

<li id="model_field_input" class="string required">
     <label for=model_field">Field<abbr title="required">*</abbr></label>
     <input type="text" value="/field/original/missing.png" size="50" name="model[field]" id="model_field"/>
</li>

Html generated whenn forced to :file (:as => :file)


  • Field*

  • When passing in a collection of strings of the label_method should still be able to be applied.

    For example when doing:

    <%= form.input :gender, :as => :select, :collection => Profile::GENDERS, :label_method => :humanize %>

    This won't work currently as in find_collection_for_column there's the optimisation:

      # Return if we have an Array of strings, fixnums or arrays
      return collection if collection.instance_of?(Array) &&
                           [Array, Fixnum, String, Symbol].include?(collection.first.class)
    

    If you remove that it works as expected.

    It might make more sense to check the existance of the label_method and the value_method and only collect if one is present.

    save_or_create_button_text is changing the I18n translation in production

    Hello guys,

    When using formtastic in development, commit button text is being created ok, ie 'Create Product' or 'Save Product'... however, while testing it in production mode, I'm having an weird issue: commit labels are updating the translation, resulting in 'Save Product Product Product'... as many times I open the form for editing.

    As translations aren't reloaded in production, changing the following code:

      I18n.t(prefix.downcase, :default => prefix, :scope => [:formtastic]) << ' ' << object_name
    

    to this:

      button_text = I18n.t(prefix.downcase, :default => prefix, :scope => [:formtastic]).dup
      button_text << ' ' << object_name
    

    worked nicely here.
    Thanks, Carlos.

    Optimizations: String handling

    I noticed two trivial and failsafe ways of optimizing Formtastic, and there are plenty of places in the current code where this applies right now:

    • Concat strings with << (append) instead of +/+= (concat)...or even better (but sometimes less readable) using interpolations. Reasons: Signficiant speed improvements, and no new unnessacy String instances.
    • Use immutable Strings, i.e. Symbol (to_sym) whenever possible (especially for: send, I18.t, etc.)

    Formtastic doing a lot of String-manipulation so... Just putting this as a note on the wall - haven't done any benchmarks on Formtastic yet but might do soon for the fun of it. 8) 1.0?

    Motivation background:
    http://blog.purepistos.net/index.php/2008/07/14/benchmarking-ruby-string-interpolation-concatenation-and-appending/

    input vs inputs arguments

    When using f.input :foo, :as => :text I get what I'd expect, a text area.
    However when using f.inputs :foo, :bar, :as => :text, I'd expect:

    (either)

    A) A text area
    B) An error to say it won't cast both :foo and :bar as a text area (Why though?)

    Unable to override :builder paramters

    If I derive a new builder from SemanticFormBuilder, I can set Formtastic::SemanticFormHelper::builder to make my new class the deafult used by SematincFormHelper::sematic_form_for, SemanticFormHelper::semantic_fields_for, etc. However SemanticFormBuilder::semantic_fields_for hardcodes :builder as Formtastic::SemanticFormBuilder, and adds it to the options with merge, which prevents overriding it when called. It would be better if the class used for :builder was settable, or reverse_merge was used so that if it is passed in the options it gets used as specified

    Showing base errors

    All errors for attributes are shown near fields, but base errors aren't shown at all?!

    Localized fieldset labels

    ...as mentioned in mailing-list:

    Localized fieldset labels, and alias :title for :name - incl. specs:

    http://github.com/grimen/formtastic/commit/effb5af778a1814dba1896e5a77f0c9981a26882

    Reason for this is that localization of form fieldset titles belong to the same domain as form labels/hints I believe, and should be treatened the same way - feels very natural. Next natural step would be localized button labels too, but I had some design issues with that as the current implementation of form buttons/actions in Formtastic is not very scalable (as discussed in the mailing-list, i.e. commit/cancel etc.) - therefore I choose not to touch that until we got a solution on "the button issue".

    Polymorphic associations problems

    In certain situations with polymorphic associations @semantic_form_for@ don't generate proper @inputs@/@semantic_fields_for@ (outputs "0" wrapped in fieldset-tag), and for other not any form at all, while this still works for Rails core helpers (@form_for@ and @fields_for@).

    I haven't found the source of all this evil yet, and simply got no simple failing spec - just a live project I'm working on. I switched to @form_for@ for these cases but will look into it of curiosity as soon as I'm done with some other stuff.

    Bug: Inline-errors gets rendered for :hidden inputs

    Inline-errors gets rendered for :hidden inputs. I believe this is incorrect behavior, as hidden fields are hidden no matter if one use CSS or not, therefore the same should apply to inline-errors on such inputs.

    Support for custom form element styles

    In order to use pretty iPhone-style checkbox toggles (Look: http://awardwinningfjords.com/ ), instead of using a plain old boolean_input (which in this case makes an erroneous assumption about how I'd like my label marked up) I had to add this custom input type:

    def iphone_toggle_input(method, options)
      html_options = options.delete(:input_html) || {}
      html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
      self.label(method, options.slice(:label, :required)) +
      self.send("check_box", method, html_options)
    end
    

    Certainly, that wasn't a big whoop (once I've ironed out the CSS-kinks), but it'd be nice to be able to just "force" the regular "input_simple" style of label markup rather than the one boolean_input now assumes.

    Child index vs. inputs block

    In the doc, the only way to interpolate through has_many with child index numbering is to use <generator>.inputs :for => <scope>. However, it doesn't accept blocks, eg. only the most basic solution is supported.

    I'd like to give block to .inputs to hand-craft the form, but I'd like to use child index numbering as well. For the first half, I have to use <generator>.semantic_fields_for, but it doesn't set params[:parent] implicitly. However, this setting is a requirement for calling parent_child_index (obviously).

    Maybe params[:parent] could be transferred in formbuilder's options somehow to the child scope.

    wiki needs an FAQ

    to address some of the common questions we've seen leading up to 1.0

    specs fail a lot with ruby 1.9.1

    There are serveral problems with ruby 1.9.1
    First it will not run any specs at all because instance vars are not allowed as block arguments

    semantic_form_for(@new_post) do |@builder|
                                     ^
    

    Im not sure if this is a typo or it makes any sense with ruby 1.8.6 because these appear only 2 times.

    After fixing this nearly all specs fail because of missing methods errors.
    The missing methods are all defined at line 43-51 in formtastic_spec.rb so I assume there is a scoping problem which I was unable to get around.

    :include_blank => false / :prompt

    I have old forms created with version = "0.1.7" after upgrade to "0.1.9"
    My selects are showing extra blank option even after :include_blank => false

    Secondly I think; if I have :prompt mentioned it should not show a blank option and should retain the :prompt even in edit form along with the selected value (as it works with :include_blank => 'My Prompt')

    Example :
    On create
    Author
    Select Author Justin French Jane Doe

    and while editing

    Author
    Select Author Justin French Jane Doe

    date ranges can't be set for date/time selects

    Before creating the input we have to check if start_date/end_date or start_time/end_time were given and parse start_day/start_month/start_year and so on from it.

    Failing example:
    f.input :my_date, :as => :date, :start_date => Date.today, :end_date => Date.today + 365

    :radio with multiple value options? (Non-association)

    I have an attr_accessor on my model that should contain one of three values. How do I code this? Doing three :as => :radio elements just creates three yes/no fields, which is not what I want.

    Maybe this is a feature request, maybe I'm just too tired to find the right docs.

    field_error_proc override breaks standard form_for error handling

    For most forms I've been using formtastic, however a few forms I don't need all the features so I'm sticking to good old form_for(@object). I've noticed that when the Formtastic gem is enabled (latest version 0.2.1), form_for no longer wraps input elements in a fieldWithErrors tag if validation has failed. Commenting out the config.gem entry and everything is back to normal.

    Using :label => false results in error

    Attempting to use :label => false results in the following error:

    You have a nil object when you didn't expect it!
    You might have expected an instance of Array.
    The error occurred while evaluating nil.+

    Use arrays instead of hashes in collection_boolean_attributes

    Hash does not preserve the order, so "No" can appear first than "Yes". This is the default line:

      { options.delete(:true) => true, options.delete(:false) => false }
    

    Just change to:

      [ [ options.delete(:true), true], [ options.delete(:false), false ] ]
    

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google โค๏ธ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.