Using Rails' form helpers without a model - ruby-on-rails

For various reasons I need to avoid traditional nested forms (nested in the sense of treating the fields like a sub-group of the primary model for the page), but still want to keep fields grouped together with an index-style naming, so I have this:
<%= simple_fields_for :crate_request do |ff| %>
<%= ff.input :_create, :label => "crate needed", :as => :boolean %>
<%= ff.input :details, :as => :text %>
<% end %>
The rendered fields are named as expected (with names like params[:crate_request][:details]) and all looks well until I submit a form with validation errors and it has to come back to re-render. The fields don't prefill with the submitted values stored in the params hash. Although I'm using simple_form, it doesn't seem to just be a simple_form issue. The native Rails helpers appear to do the same.
So the question: is there any way to have the fields automatically pre filled from the params hash again without having to manually set the value of each field from params?

You would just need to pass some object as an extra argument to simple_fields_for.
As the form builder expects the object to have field accessors as methods, but you've only got a hash (params[:create_request]), you can use OpenStruct to create an object which would translate missing method calls to hash lookup.
The final solution then would look something like this:
<%= simple_fields_for :create_request, OpenStruct.new(params[:create_request]) do |ff| %>
...
<% end %>

Replace
<%= ff.input :details, :as => :text %>
by
<%= input :details, :as => :text %>

Related

Rails - how to render two identical forms but with different element IDs?

I would like to have two almost identical forms on a single page and use a partial for generating both with a render call.
<%= render 'form_template', data: #categories_one %>
<%= render 'form_template', data: #categories_two %>
The problem lies in their only difference - both forms use f.collection_select to provide dropboxes with categories for the user.
<%= f.collection_select :category_id, data, :id, :name %>
which creates two different forms with identical IDs for the SELECT tag, breaking my forms, and I would like to avoid that. But how can I do that?
You can pass :namespace parameter to the form_for
A namespace for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id.
If you look at the documentation for collection_select, you'll see that its last argument is for HTML options. So you can do this:
<%= f.collection_select :category_id, data, :id, :name, {}, {:id => "different_css_id"} %>
Since you said this is called within a partial though, from your identical render calls, you can pass along a variable to toggle this behaviour.
# Render calls
<%= render 'form_template', data: #categories_one %>
<%= render 'form_template', data: #categories_one, :locals => {:use_other_id => true} %>
And then use that flag in your partial.

rails simple_form fields not related to the model

I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.
But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.
If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.
In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:
<%= simple_form_for #user do |f| %>
<%= f.input :agreement, as: :fake %>
....
Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.
UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
end
end
You can use attr_accessor
class Order < ActiveRecord::Base
attr_accessor :card_number
end
Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.
See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor
and here for a useful stackoverflow question What is attr_accessor in Ruby?
Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible
The best way to handle this is to use simple_fields_for like so:
<%= simple_form_for #user do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= simple_fields_for :other do |o| %>
<%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
<% end %>
<% end %>
In this example, I have added a new field called change_password which is not part of the underlying user model.
The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by #baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.
Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.
I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...
Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Example:
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
<%= devise_error_messages! %>
<% resource.class.invite_key_fields.each do |field| -%>
<%= f.input field %>
<%= hidden_field_tag :object_name, #object.class.name %>
<%= hidden_field_tag :object_id, #object.id %>
<% end -%>
I found a very simple (and somewhat strange) workaround.
Just add the input_html option with any value key inside. E.g:
= simple_form_for #user do |f|
= f.input :whatever, input_html: {value: ''}
Tested simple_from versions: 3.2.1, 3.5.1

Rails generic form for with Braintree form builder

I have a similar form like this:
<%= form_for :customer,
:params => #result && #result.params[:customer],
:errors => #result && #result.errors.for(:customer),
:builder => BraintreeHelper::BraintreeFormBuilder,
:url => Braintree::TransparentRedirect.url,
:html => { :autocomplete => "off"} do |f| -%>
First name: <%= f.text_field :first_name , :value => "John"%><br />
<%= f.submit %>
<% end %>
When I try to add the value to that text field is not letting me. However, if I take the builder from the form_for tag, I am able to do so. Anyone has had experience with Braintree form builder?
Siwei Shen is on the right track...
Form builders in Rails help you tie your forms to models in your application and generate values, names, and IDs for your form elements that correspond to model attributes. They're tremendously helpful, but sometimes it's necessary to override what they give you.
The example form builder you found in the Braintree Rails example app provides some things that are helpful since TR forms post directly to our gateway rather than your app and gives your form fields attribute names and default values that Braintree's gateway will expect as well as helps to populate validation errors which are returned to your app.
In this particular case, the form builder is ultimately merging the values for the inputs (including :first_name) with either the existing value (if any) or nil (over-writing what you've passed in.
https://github.com/braintree/braintree_ruby_examples/blob/master/rails_tr_checkout/app/helpers/braintree_form_builder.rb#L22
Feel free to modify the form builder to suit your needs!
when using "form_for", the default value is the form-object's value. e.g.
#person = Person.new(:first_name => "Jack")
<%= form_for :person do |f| %>
<%= f.text_field :first_name %>
<% end %>
and you will get a text input with the default value "Jack" :
<input type="text" value="Jack"/>
so, when using f.text_field, you are not able to set the default value, unless you change the form-object ( here is the #person).
If you want to set the default value, I suggest you either use "form_tag", which does not have "form object", or use jQuery to implement the default value stuff.

Using the textarea helper in Rails forms

Why does this code show an error in text area?
<%= form_for(:ad, :url => {:action => 'create'}) do |f| %>
<%= f.text_field(:name) %>
<%= f.text_area_tag(:text, "", :size => "50x10") %>
<%= submit_tag("Submit") %>
<% end %>
The FormHelper method is text_area, not text_area_tag.
Use either of the following:
<%= f.text_area(:text, size: '50x10') %>
or:
<%= text_area_tag(:ad, :text, size: '50x10') %>
The f variable that you are creating in the first line is a reference to your FormBuilder. By default it references ActionView::Helpers::FormBuilder or you can create your own.
The FormBuilder helper for textareas is called text_area. FormBuilder helpers are smarter than regular HTML helpers. Rails models can be nested logically, and your forms can be written to reflect this; one of the primary things FormBuilder helpers do is keep track of how each particular field relates to your data model.
When you call f.text_area, since f is associated with a form named :ad and the field is named :text it will generate a field named ad[text]. This is a parameter convention that will be automatically parsed into a Hash on the server: { :ad => { :text => "value" } } instead of a flat list of parameters. This is a huge convenience because if you have a Model named Ad, you can simply call Ad.create(params[:ad]) and all the fields will be filled in correctly.
text_area_tag is the generic helper that isn't connected to a form automatically. You can still make it do the same things as FormBuilder#text_area, but you have to do it manually. This can be useful in situations that a FormBuilder helper isn't intended to cover.

virtual model and form_for (or formtastic)

Sometimes we need form without model creation - for example search field or email, where should be send some instructions. What is the best way to create this forms? Can i create virtual model or something like this? I'd like to use formtastic, but not form_tag.
Firstly, Formtastic doesn't need a model in all cases, although it certainly works best and requires less code with a model.
Just like Rails' own built-in form_for, you can pass in a symbol instead of an object as the first argument, and Formtastic will build the form and post the params based on the symbol. Eg:
<% semantic_form_for(:session) do |f| %>
...
<% end %>
This will make the form values available to your controller as params[:session].
Secondly, a model doesn't mean an ActiveRecord model. What I mean is, Formtastic will work with any instance of a class that quacks like an ActiveRecord model.
A classic example of this that many people are using Authlogic for authentication with Formtastic. Part of Authlogic is the idea of a UserSession model, which works fine:
Controller:
def index
#user_session = UserSession.new
end
Form:
<% semantic_form_for(#user_session) do |f| %>
<%= f.input :login %>
<%= f.input :password %>
<% end %>
This will make your form data available in your controller as params[:user_session].
It's really not that hard to create a model instance to wrap up the concerns of your model. Just keep implementing the methods Formtastic is expecting until you get it working!
default_language.rb
class DefaultLanguage
attr_accessor :language_id
end
foo_controller.rb
def index
#default_language = params[:default_language] || Language.find_by_name("English")
end
index.erb
<% semantic_form_for #default_language do |form| %>
<% form.inputs :id => 'default_language' do %>
<%= form.input :id,
:as => :select,
:collection => #languages,
:required => false,
:label => "Primary Language:",
:include_blank => false %>
<% end %>
<% end %>
I used AJAX to post the form when the value changed.
Or you simply create a form with form_for and leave the model reference blank.
for example
<% form_for "", :url=>some_url do |f| %>
<%= f.text_field "some_attribute" %>
<%= submit_tag "submit" %>
You can fetch the values by simply saying params[:some_attribute] in your controller.

Resources