Can't pass through variable from collection select in rails - ruby-on-rails

I'm trying to use a collection select and from there go to my user_show page, but I can't seem to figure out how to send the selected variable through to be displayed.
Here is my current code:
<%= form_tag user_path(:id), :method => :post do %>
<%= collection_select(:user, :id, User.all, :id, :name) %>
<button type="submit">Sign In</button>
<% end %>
This is the closest I've gotten, but it is reading the :id as id. What is the correct way to do this?

Your collection_select should be like this (with explanation of each field):
collection_select(
:user, # field namespace
:user_id, # field name
# result of these two params will be: <select name="user[user_id]">
# then you should specify some collection or array of rows.
# In your example it is:
User.all,
# then you should specify methods for generating options
:id, # this is name of method that will be called for every row, result will be set as key
:name, # this is name of method that will be called for every row, result will be set as value
# as a result, every option will be generated by the following rule:
# 'user' is an element in the collection or array
)
So, change your collection_select to this:
<%= collection_select(:user, :user_id, User.all, :id, :name) %>

You probably need:
<%= form_tag user_path(:id), :method => :post do %>
<%= collection_select(:user, :user_id, User.all, :id, :name) %>
<button type="submit">Sign In</button>
<% end %>

Related

How do I select a car make and then select model according to make?

In my rails project I have two models, Car Make & Car Model, with a 1:M relationship (i.e. one Audi has many Audi models).
In my Views page, I want a form with two input fields for car make & car model. Ideally, I will be able to input a car make (i.e. Audi) and the second input field will have a drop down menu with all the models available for the make (2016 Audi A6, 2017 Audi A7).
I've set up all the relations and in the models I have saved a foreign key of the make.
currently in _form.html.erb I have
<div class="field">
<%= f.label :make_id, "Make:"%><br>
<%#= f.number_field :make_id %>
<%= f.collection_select :make_id, Make.all,
:id,:makes_info, {:include_blank => 'Please Select'} %>
</div>
<div class="field">
<%= f.label :model_id, "Model:" %><br>
<%= f.collection_select :model_id, Model.all,
:id,:model_info, {:include_blank => 'Please Select'} %>
</div>
If you want it to truly be dynamic, you would need to use an AJAX request to update the second select after the first is picked. You'd also need to use the options_for_select method inside of the select tag
Some more info to accompany what was already provided.
It's known as dynamic select boxes:
#config/routes.rb
resources :makes do
get :models, on: :collection #-> url.com/makes/models
end
#app/controllers/makes_controller.rb
class MakesController < ApplicationController
def models
#make = Make.find(params[:make][:make_id])
respond_to do |format|
format.js
end
end
end
#app/views/makes/models.js.erb
$select = $("select#models");
$select.empty();
<% #make.models.each do |model| %>
$select.append($('<option>').text(<%=j model.name %>).attr('value', <%= model.id %>));
<% end %>
#views
<%= f.collection_select :make_id, Make.all, :id, :makes_info, {include_blank: 'Please Select'}, { data: { remote: true, url: make_models_path }} %>
<%= f.collection_select :model_id, Model.all, :id,:model_info, {include_blank: 'Please Select'}, { id: "models" } %>

fields_for for has_many association using an array

Controller: project_sub_types_controller.rb
def new
#svn_repos = ['svn_software','svn_hardware']
#project_sub_type = ProjectSubType.new
#project_sub_type.repositories.build
end
Model: project_sub_type.rb
class ProjectSubType < ActiveRecord::Base
belongs_to :project_type
has_many :repositories, :dependent => :destroy
accepts_nested_attributes_for :repositories
def repositories_attributes=(attributes)
# Process the attributes hash
end
end
View: _form.html.erb
<%= form_for #project_sub_type, :html => {:class => 'project_subtype_form'} do |f| %>
<%= f.label :name, "Project sub type name" %>
<%= f.text_field :name %>
<%= f.fields_for :repositories do |ff| %>
<%= ff.label :select_svn_repositories, "Select SVN repositories" %>
<% #svn_repos.each do |repos| %>
<%= ff.check_box :repos_name, {}, "#{repos}", nil %>
<%= h repos -%>
<% end %>
<%= f.submit "Save"%>
fields_form inspect element :
<input id="project_sub_type_repositories_attributes_0_repos_name" type="checkbox" value="svn_software" name="project_sub_type[repositories_attributes][0][repos_name]">
svn_software
<input id="project_sub_type_repositories_attributes_0_repos_name" type="checkbox" value="svn_hardware" name="project_sub_type[repositories_attributes][0][repos_name]">
svn_hardware
After submitting the form the params = "repositories_attributes"=>{"0"=>{"repos_name"=>"svn_hardware"}}} even after checking both the checkboxes it is using the last selected check_box that is 'svn_hardware'
[EDIT]
Desired Output : My final output should be what the user selects so in this case it should be like this in my after submit params = "repositories_attributes"=>{"0"=>{"repos_name"=>"svn_software"}{"1"=>{"repos_name"=>"svn_hardware"}}
I believe the reason that both have 0 as a prefix is that you have solely specified one repository object, while your array (#svn_repos) contains two items. Because you only build one new object (through #project_sub_type.repositories.build), you create two checkboxes for the same model.
If you, however, were to instead do this:
# controller (inside new method)
#project_sub_type.repositories.build # 1 new object
#project_sub_type.repositories.build # 2 new objects
And then you'd have to iterate over both these objects in your _form partial, and map the names up to the #svn_repos array. I would much prefer this solution though:
# controller (inside new method)
#project_sub_type.repositories.build name: 'svn_software'
#project_sub_type.repositories.build name: 'svn_hardware'
And then iterate over the repositories in the partial, using the name attribute of the model rather than that of an array.
As Nicolay explains, the reason you have a 0 is because you build this #project_sub_type.repositories.build object once. Everything in your code is correct. But if you have to select multiple checkboxes then according to the DOCS
In View: _form.html.erb change
<%= ff.check_box :repos_name, {}, "#{repos}", nil %>
TO
<%= ff.check_box :repos_name, {:multiple => true}, "#{repos}", nil %>
Now you should be able to see the params after submit as below:
=>{"0"=>{"repos_name"=>["svn_software", "svn_hardware"]}}

Passing extra params through text_field_tag in form_for

I'm in trouble with a form I'm doing on rails 3.
What I want to do is:
I want to pass an extra parameter only to decide how many builds you want to do
to the next form. I'm trying to pass the value through a text_field_tag, but I can't get it
on the controller side.
This is what I've done:
Model:
class Story < ActiveRecord::Base
attr_accessible :resume,
:title,
:prelude,
:chapter_numbers
attr_accessor :chapter_numbers
# etc etc etc
end
View:
<%= simple_form_for(#story) do |f| %>
<div class="field">
<%= f.input :prelude, as: :text, input_html: { rows: 10, style: 'width: 100%' } %>
</div>
<div class="field">
<%= text_field_tag :chapter_numbers %>
</div>
<% end %>
the extra parameter is :chapter_numbers, which I want to catch in the controller as
params[:chapter_numbers], but it's not working. Tried to add it as virtual attribute (don't know if it's necessary)
Thanks in advance!
text_field_tag is an independent field, and won't be sent in your params
text_field_tag And text_field Are Different
You'd need to use f.text_field because this will send the required params to your controller, like this:
<%= f.text_field :chapter_numbers %>
or in your case (with simple form):
<%= f.input :chapter_numbers, as: :text %>
Good resource here about this

Rails: form_for with field for Hash

i've got model with:
class Product < ActiveRecord::Base
attr_accessible :category, :description, :img_url, :name, :price, :quantity, :tags
serialize :tags, Hash
end
and try to make form for it
<%= form_for #product do |f| %>
<%= f.label :"tags[:condition]_new", "new" %>
<%= f.radio_button :"tags[:condition]", "New", checked: true %>
<%= f.radio_button :"tags[:condition]", "Used" %>
<% end %>
unfortunately it rails raise
undefined method `tags[:condition]' for #Product:0x007fd26d965810>
<%= f.radio_button :"tags[:condition]", "Used" %> <-- ONLY FOR 2ND LINE. first is okey. WHY?!
and I can't figure out why its trying to put method on it. Has anyone idea how to make proper field for hash value?
+ Why it fails only on 2nd f.radio_button and i passes first one?
This is because you are not setting any value for 2nd radio button, try this and it will work fine.
<%= f.radio_button :"tags[:condition]", "Used", checked: false %>
As if you will not pass any value, then FormHelper class will call 'name[:condition]' method on #product to get its corresponding value, though there is no method defined in the model it raises exception.

How to edit a Rails serialized field in a form?

I have a data model in my Rails project that has a serialized field:
class Widget < ActiveRecord::Base
serialize :options
end
The options field can have variable data info. For example, here is the options field for one record from the fixtures file:
options:
query_id: 2
axis_y: 'percent'
axis_x: 'text'
units: '%'
css_class: 'occupancy'
dom_hook: '#average-occupancy-by-day'
table_scale: 1
My question is what is the proper way to let a user edit this info in a standard form view?
If you just use a simple text area field for the options field, you would just get a yaml dump representation and that data would just be sent back as a string.
What is the best/proper way to edit a serialized hash field like this in Rails?
If you know what the option keys are going to be in advance, you can declare special getters and setters for them like so:
class Widget < ActiveRecord::Base
serialize :options
def self.serialized_attr_accessor(*args)
args.each do |method_name|
eval "
def #{method_name}
(self.options || {})[:#{method_name}]
end
def #{method_name}=(value)
self.options ||= {}
self.options[:#{method_name}] = value
end
attr_accessible :#{method_name}
"
end
end
serialized_attr_accessor :query_id, :axis_y, :axis_x, :units
end
The nice thing about this is that it exposes the components of the options array as attributes, which allows you to use the Rails form helpers like so:
#haml
- form_for #widget do |f|
= f.text_field :axis_y
= f.text_field :axis_x
= f.text_field :unit
Well, I had the same problem, and tried not to over-engineer it. The problem is, that although you can pass the serialized hash to fields_for, the fields for function will think, it is an option hash (and not your object), and set the form object to nil. This means, that although you can edit the values, they will not appear after editing. It might be a bug or unexpected behavior of rails and maybe fixed in the future.
However, for now, it is quite easy to get it working (though it took me the whole morning to figure out).
You can leave you model as is and in the view you need to give fields for the object as an open struct. That will properly set the record object (so f2.object will return your options) and secondly it lets the text_field builder access the value from your object/params.
Since I included " || {}", it will work with new/create forms, too.
= form_for #widget do |f|
= f.fields_for :options, OpenStruct.new(f.object.options || {}) do |f2|
= f2.text_field :axis_y
= f2.text_field :axis_x
= f2.text_field :unit
Have a great day
emh is almost there. I would think that Rails would return the values to the form fields but it does not. So you can just put it in there manually in the ":value =>" parameter for each field. It doesn't look slick, but it works.
Here it is from top to bottom:
class Widget < ActiveRecord::Base
serialize :options, Hash
end
<%= form_for :widget, #widget, :url => {:action => "update"}, :html => {:method => :put} do |f| %>
<%= f.error_messages %>
<%= f.fields_for :options do |o| %>
<%= o.text_field :axis_x, :size => 10, :value => #widget.options["axis_x"] %>
<%= o.text_field :axis_y, :size => 10, :value => #widget.options["axis_y"] %>
<% end %>
<% end %>
Any field you add in the "fields_for" will show up in the serialized hash. You can add or remove fields at will. They will be passed as attributes to the "options" hash and stored as YAML.
I've been struggling with a very similar problem. The solutions I found here were very helpful to me. Thank you #austinfromboston, #Christian-Butske, #sbzoom, and everyone else. However, I think these answers might be slightly out-of-date. Here's what worked for me with Rails 5 and ruby 2.3:
In the form:
<%= f.label :options %>
<%= f.fields_for :options do |o| %>
<%= o.label :axis_y %>
<%= o.text_field :axis_y %>
<%= o.label :axis_x %>
<%= o.text_field :axis_x %>
...
<% end %>
and then in the controller I had to update the strong parameters like so:
def widget_params
params.require(:widget).permit(:any, :regular, :parameters, :options => [:axis_y, :axis_x, ...])
end
It seems to be important that the serialized hash parameter comes at the end of the list of parameters. Otherwise, Rails will expect the next parameter to also be a serialized hash.
In the view I used some simple if/then logic to only display the hash if it is not empty and then to only display key/value pairs where the value was not nil.
I was facing the same issue, after some research i found a solution using Rails' store_accessor to make keys of a serialized column accessible as attributes.
With this we can access "nested" attributes of a serialized column …
# post.rb
class Post < ApplicationRecord
serialize :options
store_accessor :options, :value1, :value2, :value3
end
# set / get values
post = Post.new
post.value1 = "foo"
post.value1
#=> "foo"
post.options['value1']
#=> "foo"
# strong parameters in posts_controller.rb
params.require(:post).permit(:value1, :value2, :value3)
# form.html.erb
<%= form_with model: #post, local: true do |f| %>
<%= f.label :value1 %>
<%= f.text_field :value1 %>
# …
<% end %>
No need setter/getters, I just defined in the model:
serialize :content_hash, Hash
Then in the view, I do (with simple_form, but similar with vanilla Rails):
= f.simple_fields_for :content_hash do |chf|
- #model_instance.content_hash.each_pair do |k,v|
=chf.input k.to_sym, :as => :string, :input_html => {:value => v}
My last issue is how to let the user add a new key/value pair.
I will suggest something simple, because all the time, when user will save form You will get string. So You can use for example before filter and parse those data like that:
before_save do
widget.options = YAML.parse(widget.options).to_ruby
end
of course You should add validation if this is correct YAML.
But it should works.
I'm trying to do something similar and I found this sort of works:
<%= form_for #search do |f| %>
<%= f.fields_for :params, #search.params do |p| %>
<%= p.select "property_id", [[ "All", 0 ]] + PropertyType.all.collect { |pt| [ pt.value, pt.id ] } %>
<%= p.text_field :min_square_footage, :size => 10, :placeholder => "Min" %>
<%= p.text_field :max_square_footage, :size => 10, :placeholder => "Max" %>
<% end %>
<% end %>
except that the form fields aren't populated when the form is rendered. when the form is submitted the values come through just fine and i can do:
#search = Search.new(params[:search])
so its "half" working...

Resources