Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better approach for this custom Formtastic input in Rails?

I've been using Formtastic for awhile now, and it's great for speeding up implementation of forms. However, I have a special case where I need some more customization in what's displayed in my form. Specifically, the field is a file upload form for uploading images, and on the edit form, I want to show a thumbnail of the current version of the image that has been uploaded.

Desired Form Output

I've got this working, but it required that I use custom HTML markup, which means that any time Formtastic changes the output format, I need to update my matching HTML. Here's what I've got right now:

<%= form.inputs do %>
    <% if form.object.new_record? -%>
        <%= form.input :image, :required => true, :hint => 'Maximum size of 3MB. JPG, GIF, PNG.' %>
    <% else -%>
        <li class="file input required" id="profile_image_input">
            <label class="label" for="profile_image">Image</label>
            <%= image_tag form.object.image.url(:thumb), :class => 'attachment' %>
            <%= form.file_field :image %>
            <p class="inline-hints">Maximum size of 3MB. JPG, GIF, PNG.</p>
        </li>
    <% end -%>
<% end %>

Ideally, it would be nice to do something more like the following, where input_html is assumed to be the generated HTML for the input, hint, etc.:

<%= form.inputs do %>
    <%= form.input :image, :required => true, :hint => 'Maximum size of 3MB. JPG, GIF, PNG.' do |input_html| %>
        <%= image_tag form.object.image.url(:thumb), :class => 'attachment' unless form.object.new_record? %>
        <%= input_html %>
    <% end %>
<% end %>

Does anything like this already exist? Or is there another similar option that will make my life easier?

like image 725
Matt Huggins Avatar asked Mar 26 '12 15:03

Matt Huggins


2 Answers

Well, I solved this myself of course. As always happens when I post here. :P

For anyone looking to do something similar, I created a custom input type derived from Formtastic's file input.

class AttachmentInput < Formtastic::Inputs::FileInput
  def image_html_options
    {:class => 'attachment'}.merge(options[:image_html] || {})
  end

  def to_html
    input_wrapping do
      label_html <<
      image_html <<
      builder.file_field(method, input_html_options)
    end
  end

protected

  def image_html
    return "".html_safe if builder.object.new_record?

    url = case options[:image]
    when Symbol
      builder.object.send(options[:image])
    when Proc
      options[:image].call(builder.object)
    else
      options[:image].to_s
    end

    builder.template.image_tag(url, image_html_options).html_safe
  end
end

Now I can just create an input of this type in the following manner:

<%= form.input :image, :as => :attachment,
                       :required => true,
                       :hint => 'Maximum size of 3MB. JPG, GIF, PNG.',
                       :image => proc { |o| o.image.url(:thumb) } %>

Optionally, the :image tag can accept one of:

  • a Proc, which passes the form's object param,
  • a Symbol, which is a method name on the object,
  • anything else, which is converted to a string and assumed to represent the URL.

Additionally, I can utilize the :image_html option for specifying HTML classes, id's, etc.

like image 99
Matt Huggins Avatar answered Nov 12 '22 23:11

Matt Huggins


At the bottom of the Formtastic docs at https://github.com/justinfrench/formtastic#modified--custom-inputs:

Create a file in app/inputs with a filename ending in _input.rb

Not enough for a full solution, but after rooting through the formtastic source for inspiration was able to come up with the following which is working well for me.

in app/inputs/label_input.rb:

class LabelInput
  include Formtastic::Inputs::Base

    def to_html
        input_wrapping do
            label_html <<
            "#{@object.send(method)}"
        end
    end
  end

happen to be using ActiveAdmin, in form on page:

form do |f|
  f.inputs do
    f.input :project
    f.input :date_consumed
    f.input :total_consumed
    f.input :computed_waste, :as => :label
    f.actions
  end
end
like image 7
John Michael Pirie Avatar answered Nov 13 '22 00:11

John Michael Pirie