SimpleForm without for (non model form)
Asked Answered
T

6

86

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

https://github.com/plataformatec/simple_form

Third answered 3/3, 2011 at 13:13 Comment(2)
Maybe [my answer to the other question][1] helps you. [1]: https://mcmap.net/q/243682/-rails-simple_form-fields-not-related-to-the-modelCrossopterygian
Check out #12146966Yuriyuria
R
132

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>
Rabbi answered 3/3, 2011 at 13:56 Comment(9)
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.Ultann
@Ultann Maybe that's not a good example. You can use f.input :name, :as => :string and generate your usual form with label, hints, etc.Rabbi
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.Ultann
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.Overleap
I don't understand why this is the accepted answer, because the answer is just wrong.Ceolaceorl
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?Jedjedd
Yeah, I think I missed the point previously, and I've written a better answer as comment. I've updated the answer so it does what simple_form is supposed to do.Rabbi
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.Morganne
This worked for me, but had to include input_html: { value: nil } to avoid undefined method 'name' errorKeary
O
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.

Overleap answered 2/7, 2013 at 19:38 Comment(3)
This worked for me: <%= f.input :create_key, input_html: {value: nil}, required: true, hint: "by invite only, see email for key" %>Jedjedd
If working with a collection, use selected instead of value in order to avoid the undefined method 'name' for Model errorColas
" simple_form relies on using a model" thanks this is exactly what I was looking for :)Promptbook
W
18

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

Whiffler answered 5/8, 2016 at 16:2 Comment(1)
this is the answer I was looking forAcetal
I
5

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
Insouciance answered 6/3, 2013 at 23:59 Comment(1)
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.Overleap
P
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}" }
Pathetic answered 17/11, 2014 at 17:44 Comment(0)
K
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] %>

Kuo answered 7/3, 2022 at 10:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.