Rails pass object through form select - ruby-on-rails

In my form:
<%= f.select :user, User.all %>
This generates a select with a collection of all users.
When I hit submit, it's currently passing the object as a string, looks like below:
[3] pry(#<PermissionFormsController>)> params[:permission_form][:user]
=> "John Doe <John.Doe#foo.com> - Bar (something)"
and
[4] pry(#<PermissionFormsController>)> params[:permission_form][:user].class
=> String
I'd like to get the object rather, like current_user
[5] pry(#<PermissionFormsController>)> current_user
=> Homer Simpson <homer#gmail.com> - Foo (admin)
Which is an object of class User:
[6] pry(#<PermissionFormsController>)> current_user.class
=> User(id: integer, status: string, ...)
Is there a way to achieve this?

You cannot pass Ruby objects directly from a form to the server, only strings. Your controller is responsible for turning this data into actual models. This is normally done by passing the object id and calling YourModel.find in the controller.
So you need to change your form like this:
<%= f.select :user_id, User.all.map{|u| [u.to_s, u.id]} %>
And then in your controller's action:
user = User.find(params[:permission_form][:user_id])
Hope this helps.

Related

getter not getting values for new record on validation rails 4

I have a form_for for creating a new record. I have set getter and setter methods to access form field in my view. Below are my getter ans setter methods with my form view,
Getter & Setter Methods respectively :
def manufacturer_model_name
self.manufacturer_models.pluck(:name).join(', ') unless self.manufacturer_models.blank?
end
def manufacturer_model_name=(names)
names = names.split(',').map{|n| n.strip}.delete_if(&:empty?) if names.present?
names.uniq.each do |name|
id = ManufacturerModel.where(:name => name, :manufacturer_id => manufacturer.id).first_or_create.id
if self.new_record?
self.user_skill_manufacturer_models.build(:user_skill_id => self.id, :manufacturer_model_id => id)
else
self.user_skill_manufacturer_models.where(:user_skill_id => self.id, :manufacturer_model_id => id).first_or_create.id
end
end if names.present?
end
Form View:
= f.text_field :manufacturer_model_name, :class => 'form-control
My problem is that, my input field is autocomplete with multiple set to true, to get multiple values. when user enters multiple comma separated values and submits form and if there are any errors on the form, my new action is rendered and user losts all the entered values forcing him to reenter all again. How can I solve this problem?
It would be better to make a manufacturer_model_name_form field or some such via attr_accessor, and then parse that in validate. It would look something like this:
class ManufacturerModel < ActiveRecord::Base
attr_accessor :manufacturer_model_name_form
validate :validate_manufacturer_model_name
def validate_manufacturer_model_name
errors.add(:manufacturer_model_name, 'not a valid manufacturer model name') if !!true # perform your validation here in place of `!!true`
end
end
Then, in your form, you would use manufacturer_model_name_form instead of manufacturer_model_name:
= f.text_field :manufacturer_model_name_form, :class => 'form-control'

rails form for model select box with 1-dimensional data

I want to limit the entry possibilities for a text field in my model to a previously defined array.
How do I make an options_for_select with just a 1-dimensional array like ["foo","bar","foobar"]?
I tried
form_for #mappings do |f|
f.select(:mapping_type, options_for_select(["foo","bar","foobar"]), class: "..."
end
But the select box comes out all messed up like this:
<select name="section_mapping[mapping_type]" id="section_mapping_mapping_type">
as opposed to what it should be:
<select name="mapping_type" >
EDIT:
I changed the f.select to select_tag and the form shows up without any errors but when I submit it, it leaves that field empty
EDIT 2:
f.collection_select(:mapping_type, options_for_select([...]), class: "..."
works as in it submits the form with the value correctly, but the HTML class is not applied. Why is that?
Basically, you want to be able to tie your collection select to a property of the object (in your case, #mappings)
Also, from the doc on rails collection_select, it will take options as follow:
collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {}) public
Objet: the object you are binding the selected option to (#mappings [f]) in this case
method: The property/attribute of the object (in this case, mapping_type)
collection: The collection for select ( ["foo","bar","foobar"] )
value_method: The value you want to send back with the submit (Note that this is a method which means you should be able to call it on an object.) more on this later.
text_method: The value you want to show as text on the select option on the view (this is also a method as above, more on this later as well)
options: any additional option you want, (e.g: include_blank)
html_options: eg: id, class etc.
As concerning the value_method and text_method, these are methods that should be called on your collection, which means that your collection will be an array of objects.
To this end, you can have the following:
class CollectionArr
include ActiveModel::Model
attr_accessor :name
ARR = [
{"name" => "foo"},
{"name" => "bar"},
{"name" => "foobar"}
]
def self.get_collection
ARR.collect do |hash|
self.new(
name: hash['name']
)
end
end
end
From here, calling CollectionArr.get_collection will return an array of objects where you can cal .name to return either foo, bar, or foobar. This makes using the collection_select and easy deal from here:
<%= f.collection_select : mapping_type, CollectionArr.get_collection, :name, :name, {:include_blank => "Select one"} %>
And all is green...

Cannot pass parameters to controller

I have the following haml code in index.haml
%h1.content-header{id: 'content-header'} HEADER
%section.form
%form.standard{action: some_path, method: 'get'}
%fieldset.search{'aria-labelledby' => 'content-header'}
%input{type: 'search', name: 'name', placeholder: 'something', role: 'textbox'} -----(6)
%fieldset.input-actions
%input.primary-action{type: 'submit', value: 'search' , name: 'invokeSearch'}
I have the following in my controller
def index
Rails.logger.debug "#{params[:name]}"
unless #invalid_characters
unless params[:name].blank?
....
....
....
The issue is if i change the name: 'name' in line 6 to say name: 'test' .... params[:test] is always blank in my controller. But if i use name: 'name' , params[:name] seems to work . No clue what i am doing wrong here
If you are using rails you should probably benefit from using one of the form helpers for generating the form. For example forms_for that makes you access things in your model directly. It may look like this.
= forms_for #thing do |f|
= f.text_field :name
= f.text_field :test
= f.submit
In this case there will be a map for the model instance in the params map. Something like
params[:thing][:name]
or
params[:thing][:test]
you can take that map and pass it into a model to create or update it. If you don't want the tight coupling with the model there is the form_tag method that does much the same thing except you need to be more explicit in what value goes where. And fields ends up directly in params instead of in a model hash.
To see how your params come in you can use the inspect method.
puts "params: #{params.inspec}"
or install pry to set a break point with
binding pry
Some ideas. Hope it works out for you. I think you should start with trying to use the rails forms helpers and it will probably sort itself out.

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

How to send user.id with :user in params even though user.name is what is in the form drop down?

Currently, I have an action in my customers controller generating an array of names, #username_array, of all objects of class User with which to populate a drop down menu in a form that creates a new object of class Customer. The form element looks like this right now:
<%= f.select :user_id, #username_array %>
What I'd really like is for the id of the user to be sent into params[:customer][:user_id] instead of the name of that user that is chosen in the drop down. So in my create action in my customers controller I have the following code:
#customer = Customer.new(params[:customer])
#user = User.find_by_name(params[:customer][:user_id]) # where :user_id is actually currently the name selected from the drop down
#customer.user_id = #user.id
Is there an easier way of doing this?
Change your #username_array to include both the name and the id of the user:
Instead of:
["Bob","Sally","Dave"]
Make it:
[["Bob", 1],["Sally",2],["Dave",3]]
This could be accomplished by something like this:
#username_array = User.all.map {|user| [user.name, user.id]}
Then, f.select will display the name in the dropdown, but the actual value passed in through params[:customer][:user_id] will be the id of the user, which is what you want. With this in place, the following is all you need in the controller action code:
#customer = Customer.new(params[:customer])
You won't have to look up the user by name, the params hash will already have the correct id value.
Note that instead of making #username_array an instance variable you could just create a utility method in the helper for this controller, or the application helper:
def user_select
User.all.map {|user| [user.name, user.id]}
end
Then, in your view:
<%= f.select :user_id, user_select %>
If you put this in your application helper, you can use it everywhere and only have the code in one place (DRY).
you can do
#user = User.find_by_name(params[:customer][:user_id])
#user.customers.new(params[:customer])
or
#user = User.find_by_name(params[:customer][:user_id])
#customer = #user.customers.create(params[:customer])
but to do that you must have the relation (has_many, belongs_to,...)
or
Customer.new(params[:customer], :user_id => params[:customer][:user_id])
or
f.collection_select :user_id, #username_array, :id, :name

Resources