Rails: Form helper for enum attribute - ruby-on-rails

I set category's viewable attribute as an enum
class Category < ActiveRecord::Base
enum viewable: [:only_self, :friends, :anyone]
end
How should I make them accesible in _form when users edit this attribute? Something like?
<%= form_for(#category) do |f| %>
<%= f.select(:viewable) %>
<% end %>
UPDATE------
<%= f.select(:viewable, options_for_select([["description1", "only_self"], ["description2", "friends"], ["description3", "anyone"]])) %>
The description for each is quite repetative, because I need to put them whenever I need to display, not just in the forms. Where should I put them?
In form_for, the f.select does not display the current value of the this field. It always is the first description1.

When using the plural form, rails provides the full key/value array, so you can call Category.viewables for the array, and with the help of options_for_select you'll get a nice functioning dropdown list
<%= form_for(#category) do |f| %>
<%= f.select(:viewable, options_for_select(Category.viewables)) %>
<% end %>
Updated answers
The description for each is quite repetative, because I need to put
them whenever I need to display, not just in the forms. Where should I
put them?
You can use the I18n library, assuming your locale is chinese (zh i think) then you could create /config/locales/zh.yml and add something like this
categories:
viewable:
only_self: 'some chinese text'
friend: 'more chinese text'
anyone: 'well you know'
Then better create some helper that returns the localized options
def options_for_viewables
{
t('categories.viewable.only_self') => 0,
t('categories.viewable.friends') => 1,
t('categories.viewable.anyone') => 2
}
end
The view will become like this
<%= f.select(:viewable, options_for_viewables) %>

Related

Form Design: How to best display/add different statuses?

I am working on a rails form. Essentially, a person can have multiple statuses and switch between the different statuses. In database table, the display will be simple as follows:
status start_date end_date
work 1/1/15 1/10/15
sick 1/11/15 2/15/15
work 2/16/15 3/15/15
sick 1/15/15 1/14/15
I need to prompt user to input these information. I have made a status class which belongs to a person class. So basically, these fields will be a part of nested forms.
My question is: How can I dynamically display these information to make forms elegant and clean to use?
Thanks!
If I understood your domain, your Person has many Status, right?
The simplest way to do it is use the gem cocoon. Your view will look like this:
<%= form_for #person do |person_form| %>
<%= person_form.input :name %>
<%= person_form.fields_for :statuses do |status_form| %>
<%= status_form.field :start_date, :end_date %>
<!-- cocoon's method to dynamically add nested forms -->
<%= link_to_add_association 'add status', person_form, :statuses
<% end %>
<% end %>

How to access nested child element in fields_for

I'm trying to access a Hash type of mongoid in fieds_for and I already have a relationship with a model and want to access a hash of that model. Something like:
class Leave
field :leaves_types, :type => Hash
end
class User
has_many :leaves
end
Want to do something like:
form_for #user do |f|
f.fields_for :leaves.leave_types...
How I can achieve this? Thanks in advance.
You should give a block to fields_for. For more information on that method see docs. In your case, first, add this line to your User model:
class User
has_many :leaves
accepts_nested_attributes_for :leaves
end
This is required so that when your form is posted, the attributes coming from the form fields for leaves via params were handled correctly.
Now your template should look like this (for simplicity by now I assume that your Leave also has a simple text field named foo):
<%= form_for #user do |f| %>
...
<%= f.fields_for :leaves do |leave_fields| %>
# Fields for a leave here ----v
Foo: <%= leaves_fields.text_field :foo %>
<% end %>
<% end %>
Or, if you #user.leaves already initialized and you want form builder to put its values to form fields, you have to iterate over #user.leaves, passing each of them to fields_for as second argument:
<%= form_for #user do |f| %>
...
<% #user.leaves.each do |leave| %>
<%= f.fields_for :leaves, leave do |leave_fields| %>
# Note the addition here --^
Foo: <%= leave_fields.text_field :foo %>
<% end %>
<% end %>
<% end %>
But your question has another one inside: you have not a text field, but a hash, and there is no default form input for it (i.e. there is no f.hash_field :leaves_types). So you may want to make it by yourself like suggested in these questions: [1], [2] and [3].
Anyway, having a Hash field seems rather uncommon to me, so maybe Hash can be somehow replaced, say, with another has_many association (not sure), and in this case you will only need another nested fields_for.

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 Multiple Entries in a form

I'll try and explain this as much and as easily as possible.
I have a Rails form, and 3 models.
Models: DemoModule, SalesDemo, and SalesDemoModule
What I want to do in my view/form is create a new SalesDemo, but a SalesDemo has many SalesDemoModules.
In the controller I have:
#sales_demo = SalesDemo.new
#demo_modules = DemoModule.find(:all, :conditions => ['active = true'])
How can I, in my view, have a text field row for each DemoModule, which I can pass back to the controller action, to save into SalesDemoModule?
You can specify that the SalesDemo accepts_nested_attributes_for SalesDemoModule, which then allows you to created a nested form (i.e. within a form_for a SalesDemo, you can have fields_for SalesDemoModule). Here's a simple example.
Simply put:
<%= form_for #sales_demo do |sales_demo_form| %>
<%= sales_demo_form.text_field "some_sales_demo_property" %>
<%= sales_demo_form.fields_for #demo_modules do |modules| %>
<%= modules.text_field "some_module_text_field" %>
<% end %>
<% end %>
In the SalesDemo, you will need to have
accepts_nested_attributes_for :demo_modules
You can get some more information here.

Rails input from view

In rails how do you get information from view to use in a controller. Like if i have a page with a text field and a button, how would i send the value from the field (without a model) into my controller to work with it in one of my functions. Im using rails 3
Sounds like you could use a simple form, for example:
in your views/products/index.html.erb:
<% form_tag omg_products_path do %>
<%= text_field_tag :my_input %>
<%= submit_tag "Send input" %>
<% end %>
in your controllers/products_controller.rb:
def omg
my_input = params['my_input']
#do whatever you want with my_input
end
You will also want to configure routes.rb, for example like this:
resources :products do
post :omg, :on => :collection
end
I think you will still want a model, but just use a Non Active Record Model see this Rails Cast: http://railscasts.com/episodes/121-non-active-record-model

Resources