85

Is it possible to use Simple Form (by: Plataformatec) without a model?

https://github.com/plataformatec/simple_form

Bruno Casali
  • 1,339
  • 2
  • 17
  • 32
Edward Ford
  • 1,631
  • 3
  • 13
  • 25
  • Maybe [my answer to the other question][1] helps you. [1]: http://stackoverflow.com/a/14659270/157816 – baxang Feb 02 '13 at 06:32
  • Check out http://stackoverflow.com/questions/12146466/rails-simple-form-fields-not-related-to-the-model – netwire Aug 13 '13 at 02:11

6 Answers6

129

You can use :symbol as the first argument.

<%= simple_form_for :user, url: users_path do |f| %>
  <%= f.input :name, as: :string %>
  ...
<% end %>

It will output something like this:

<form novalidate="novalidate" class="simple_form user" action="/users" accept-charset="UTF-8" method="post">
  ...
  <div class="input string required user_name">
    <label class="string required" for="user_name">
      <abbr title="required">*</abbr> Name
    </label>
    <input class="string required" type="text" name="user[name]" id="user_name" />
  </div>
  ...
</form>
htanata
  • 36,666
  • 8
  • 50
  • 57
  • Kind of pointless though isn't it? You don't get any of the benefits of simple_form_for when you're using the fallback textfield generators. – toxaq Jul 20 '12 at 23:23
  • 4
    @toxaq Maybe that's not a good example. You can use `f.input :name, :as => :string` and generate your usual form with label, hints, etc. – htanata Jul 20 '12 at 23:48
  • 9
    Oddly that's exactly what I'm trying to do and having no success. Your example gives an `undefined method 'name?' for nil:NilClass` for me at least. – toxaq Jul 21 '12 at 00:11
  • 11
    This option still uses a model under the covers. The symbol should match a model name, it will resolve to a model and create a new one and use it on every field, so the properties used in the form fields should actually exists in the model. – Miguel Madero Jul 02 '13 at 19:32
  • 22
    I don't understand why this is the accepted answer, because the answer is just wrong. – Phillipp Sep 16 '14 at 14:27
  • Well, I won't say the answer is wrong, but it seems to miss the whole point of the question. Unless I'm missing it? – codenoob Nov 09 '14 at 17:11
  • 1
    Yeah, I think I missed the point previously, and I've written a better answer as [comment](http://stackoverflow.com/questions/5181143/simple-form-without-for-non-model-form/5181627?noredirect=1#comment15335055_5181627). I've updated the answer so it does what simple_form is supposed to do. – htanata Nov 10 '14 at 17:37
  • Still problem is how to autofill those inputs, if form is used for report generation for example. Tried to used hash instead of ActiveRecord object with no luck. – nazar kuliyev Nov 24 '15 at 08:21
  • This worked for me, but had to include `input_html: { value: nil }` to avoid `undefined method 'name'` error – AlexGuti Mar 21 '16 at 11:49
19

Unfortunately simple_form relies on using a model. Essentially it would be nice to have something like simple_form_tag and input_tag methods equivalent to their rails *_tag helpers. Until then, there's an easy work around.

Use a symbol instead of the class in the form and pass the value explicitly to prevent simple_form from trying to access the model properties.

<%= simple_form_for :user, :url => '/users' do |f| %>
  <%= f.text_field :name, input_html: { value: nil } %>
<% end %>

This will avoid the undefined method 'name' for User error.

Miguel Madero
  • 1,948
  • 13
  • 21
  • 2
    This worked for me: <%= f.input :create_key, input_html: {value: nil}, required: true, hint: "by invite only, see email for key" %> – codenoob Nov 09 '14 at 17:44
  • 1
    If working with a collection, use `selected` instead of `value` in order to avoid the `undefined method 'name' for Model` error – fkoessler Nov 02 '15 at 10:17
  • " simple_form relies on using a model" thanks this is exactly what I was looking for :) – Olivier Girardot Aug 14 '21 at 14:45
17

You can also use fields outside the model within a form model, with simple_fields_for like this:

<%= simple_form_for @user do |f| %>
  <%= f.input :name %>

  <%= simple_fields_for :no_model_fields do |n| %>
    <%= n.input :other_field %>
  <% end %>
<% end %>

This is simple and practical solution, because you can create different kind of fields from different models or without using models

jacr1614
  • 1,250
  • 2
  • 14
  • 23
4

You could also pass a :symbol instead of @object as argument for simple_form_for.

<%= simple_form_for :email, :url => '/post_email' do |f| %>
  <%= f.input :subject, :as => :string %>
<% end %>

Which would output:

<form method="post" class="simple_form email" action="/post_email" accept-charset="UTF-8">
  ...
  <input type="text" size="30" name="email[subject]" id="email_subject">
</form>

Please be aware of following draw-backs:

  • You won't be able to take advantage of automatic model validation
  • Need to explicitly define :url and the type of each input
Trung Lê
  • 5,188
  • 5
  • 27
  • 26
  • 3
    This option still uses a model under the covers. The symbol should match a model name, it will resolve to a model and create a new one and use it on every field, so the properties used in the form fields should actually exists in the model. – Miguel Madero Jul 02 '13 at 19:33
4

All of the methods above still leave you with form data nested inside of "user" or whatever symbol that you pass as the first argument. That's annoying.

To mimic simple_form's style/benefits, but remove the object/symbol dependency and the forced data nesting, you can create a partial.

HAML examples:

form view:

= form_tag("path/to/action", method: "POST") do
    = render "path/to/partial/field", type: "string", required: true, item: "first_name"

field partial:

- required_string = required ? "required" : ""
%div{class: "input #{type} #{required_string} #{item}"}
  %label{class: "#{type} #{required_string}", for: "#{item}"}
    - if required
      %abbr{title: "required"}
        *
    = t("application.#{item}")
  %input{name: "#{item}",                                                     |
    placeholder: t("application.#{item}"),                                    |
    type: "#{type}",                                                          |
    required: required,                                                       |
    "aria-required" => "#{required}" }
FreePender
  • 4,770
  • 2
  • 18
  • 15
1

In the case that you want to add a field that is not part of your model in your form, so that you don't want to read attributes, simple_form propose to use what they call a fake input in their wiki.

String Input

app/inputs/fake_input.rb:

class FakeInput < SimpleForm::Inputs::StringInput
  # This method only create a basic input without reading any value from object
  def input(wrapper_options = nil)
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
    template.text_field_tag(attribute_name, nil, merged_input_options)
  end
end

Then you can do <%= f.input :thing, as: :fake %>

Boolean Input

app/inputs/fake_checkbox_input.rb:

class FakeCheckboxInput < SimpleForm::Inputs::StringInput
  # This method only create a basic input without reading any value from object
  def input(wrapper_options = nil)
    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
    tag_name = "#{@builder.object_name}[#{attribute_name}]"
    template.check_box_tag(tag_name, options['value'] || 1, options['checked'], merged_input_options)
  end
end

Then you can do <%= form.input :remove_avatar, as: :fake_checkbox, wrapper: :vertical_boolean %>

Select

app/inputs/fake_select_input.rb:

class FakeSelectInput < SimpleForm::Inputs::CollectionSelectInput
  def input(wrapper_options = nil)
    label_method, value_method = detect_collection_methods

    merged_input_options = merge_wrapper_options(input_html_options, wrapper_options).merge(input_options.slice(:multiple, :include_blank, :disabled, :prompt))

    template.select_tag(
      attribute_name, 
      template.options_from_collection_for_select(collection, value_method, label_method, selected: input_options[:selected], disabled: input_options[:disabled]), 
      merged_input_options
    )
  end
end

Then you can do <%= f.input :thing, as: :fake_select, collection: my_collection, selected: params[:thing] %>

Emilien Baudet
  • 428
  • 4
  • 10