Skip to content

Adding custom input components

JamesGecko edited this page Mar 23, 2012 · 11 revisions

You may find yourself wanting to add a custom component to the normally generated set of tags that simple_form creates for each input.

In my case I wanted to show a preview of an uploaded file below the file input. This would show when editing a record with an existing file, or when an error has occured with another part of the form when adding a new record but where a file has been uploaded against that record successfully. See: carrierwave allows you to keep the file in a temporary cache in case of form validation problems on other fields.

SimpleForm 2.0

First, create a subclass of SimpleForm::Inputs::FileInput under app/inputs:

# app/inputs/image_preview_input.rb
class ImagePreviewInput < SimpleForm::Inputs::FileInput
  def input
    # :preview_version is a custom attribute from :input_html hash, so you can pick custom sizes
    version = input_html_options.delete(:preview_version)
    out = '' # the output string we're going to build
    # check if there's an uploaded file (eg: edit mode or form not saved)
    if object.send("#{attribute_name}?")
      # append preview image to output
      out << template.image_tag(object.send(attribute_name).tap {|o| break o.send(version) if version}.send('url'))
    end
    # append file input. it will work accordingly with your simple_form wrappers
    (out << @builder.file_field(attribute_name, input_html_options)).html_safe
  end
end

Then, use it in your form:

<%= simple_form_for @some_model do |f| %>
  <!-- we specify the new custom input, and we'll use the 'thumb' version of the carrierwave upload -->
  <%= f.input :image, :as => :image_preview, :input_html => {:preview_size => :thumb} %>
<% end %>

If you need a better upload input specifically, you might want to check out the fancy uploads gem.

SimpleForm 1.x

Your first step is to modify the simple_form initialiser setting config.components to call your custom component:

config.components = [ :placeholder, :label, :hint, :input, :preview, :error ]

You'll find a commented out version of this config option at the top of the file in /config/initializers/simple_form.rb.

As you can see I've added a component called preview that will show after the input. In my example I've also split out the label and input components (normally label_input) because it suited my form design better.

The next step is to require a file that we're going to store in your /lib folder that will deal with displaying your custom component. Put it right at the end of the same file /config/initializers/simple_form.rb.

    SimpleForm.setup do |config|
      config.components = [ :placeholder, :label, :hint, :input, :preview, :error ]
    end
    
    require 'simple_form/preview_component.rb'

Ok now simply make a folder in your /lib folder called simple_form and make a new file in there called preview_component.rb. Here is the contents of my preview_component.rb file:

    module SimpleForm 
      module Components 
        module Preview 
          def preview 
            if input_type == :file
              template.image_tag(object.send("#{attribute_name}_url"), :class => 'file_preview') if object.send("#{attribute_name}?")
            end
          end 
        end 
      end 
      module Inputs 
        class Base 
          include SimpleForm::Components::Preview 
        end 
      end 
    end

In this file we're essentially opening SimpleForm::Inputs::Base and including SimpleForm::Components::Preview. This module contains a method preview that first checks if the input we're dealing with is a file input, and if so, adds the preview image.

You have access to template which allows you to use view helpers, object which is the overall record object, and attribute_name which is the name of the attribute currently being dealt with. There are more variables and objects at your disposal, see: https://github.com/plataformatec/simple_form/blob/master/lib/simple_form/inputs/base.rb#L18

Many thanks to Carlos Antonio da Silva who provided me with most of this information to get me started.