Skip to content

Twitter Bootstrap v2 and simple_form v2

XsErG edited this page Feb 17, 2013 · 2 revisions

Twitter Bootstrap v2 and SimpleForm v2

To better integrate Twitter Bootstrap v2 with simple_form v2 I use the following updates:

The tweaks required takes 2 parts:

  1. The setup for input wrappers and form classes, ...
  2. The buttons wrappers to be added easily

Setup for input wrappers and form classes, ...

Most of the work can be done by generating your simple_form config with as describe on simple_form documentation

rails generate simple_form:install --bootstrap

I had to make a few extra tweaks to use the horizontal mode, that I prefer:

config.label_class = 'control-label'
config.form_class = 'simple_form form-horizontal'

That's now good for all except the submit buttons, I'm still not happy to define the whole submit wrapper on each form, so let's make a button wrapper.

Buttons wrappers

Adding the following to a new library, for example /lib/extras/simple_form_extensions.rb:

module WrappedButton
  def wrapped_button(*args, &block)
    template.content_tag :div, :class => "form-actions" do
      options = args.extract_options!
      loading = self.object.new_record? ? I18n.t('simple_form.creating') : I18n.t('simple_form.updating')
      options[:"data-loading-text"] = [loading, options[:"data-loading-text"]].compact
      options[:class] = ['btn btn-primary', options[:class]].compact
      args << options
      if cancel = options.delete(:cancel)
        submit(*args, &block) + ' ' + I18n.t('simple_form.buttons.or') + ' ' + template.link_to(I18n.t('simple_form.buttons.cancel'), cancel)
      else
        submit(*args, &block)
      end
    end
  end
end
SimpleForm::FormBuilder.send :include, WrappedButton

will allow you to use:

<%= f.button :wrapped, :cancel => posts_path %>

or if you prefer a cancel button, the if block could be

if cancel = options.delete(:cancel)
  submit(*args, &block) + ' ' + I18n.t('simple_form.buttons.or') + ' ' + template.link_to(template.button_tag(I18n.t('simple_form.buttons.cancel'), :type => "button", :class => "btn"), cancel)
else
  submit(*args, &block)
end

Don't forget to load your new library, I like to use the following way from config/application.rb :

config.autoload_paths += %W(#{config.root}/lib/extras)

And require it from config/initializers/simple_form.rb

require 'simple_form_extensions'

Last thing, add the new translations within config/locales/simple_form.en.yml

en:
  simple_form:
    creating: "Creating..."
    updating: 'Updating...'
    buttons:
      or: 'or'
      cancel: 'cancel'

Restart your app server and here we go, we now keep the really clean way to manage forms of simple_form with the beautiful design of Twitter Bootstrap.


Optional: Handling Bootstrap modal dialogs

If you use the button wrapper inside a Bootstrap modal dialog, you will probably want the Cancel link to close the modal, not redirect to another location. In this case, use the following as your /lib/extras/simple_form_extensions.rb:

module WrappedButton
  def wrapped_button(*args, &block)
    template.content_tag :div, :class => "form-actions" do
      options = args.extract_options!
      loading = self.object.new_record? ? I18n.t('simple_form.creating') : I18n.t('simple_form.updating')
      options[:"data-loading-text"] = [loading, options[:"data-loading-text"]].compact
      options[:class] = ['btn btn-primary', options[:class]].compact
      args << options
      if cancel = options.delete(:cancel)
        if cancel == "close-modal"
          submit(*args, &block) + ' ' + I18n.t('simple_form.buttons.or') + ' ' +
            template.link_to(I18n.t('simple_form.buttons.cancel'), "#",
                             { "data-dismiss" => "modal", "aria-hidden" => "true" })
        else
          submit(*args, &block) + ' ' + I18n.t('simple_form.buttons.or') + ' ' +
            template.link_to(I18n.t('simple_form.buttons.cancel'), cancel)
        end
      else
        submit(*args, &block)
      end
    end
  end
end
SimpleForm::FormBuilder.send :include, WrappedButton

Use the wrapper as usual for normal forms:

<%= f.button :wrapped, :cancel => posts_path %>

However, when you want the Cancel link to close a modal, pass in "close-modal" (with quotation marks) as the cancel value:

<%= f.button :wrapped, :cancel => "close-modal" %>