A Many-Many Association Hidden_Field edit problem. ROR - ruby-on-rails

I'm trying to pass an array into a hidden_field.
The following User has 3 roles [2,4,5]
>> u = User.find_by_login("lesa")
=> #<User id: 5, login: "lesa", email: "lesa.beaupry#gmail.com", crypted_password: "0f2776e68f1054a2678ad69a3b28e35ad9f42078", salt: "f02ef9e00d16f1b9f82dfcc488fdf96bf5aab4a8", created_at: "2009-12-29 15:15:51", updated_at: "2010-01-06 06:27:16", remember_token: nil, remember_token_expires_at: nil>
>> u.roles.map(&:id)
=> [2, 4, 5]
Users/edit.html.erb
<% form_for #user do |f| -%>
<%= f.hidden_field :role_ids, :value => #user.roles.map(&:id) %>
When I submit my edit form, I receive an error: ActiveRecord::RecordNotFound in UsersController#update "Couldn't find Role with ID=245"
Next I attempted to edit my users/edit.html.erb to:
<%= f.hidden_field :role_ids, :value => #user.roles.map(&:id).join(", ") %>
Yielding what I believe is a string: "role_ids"=>"2, 4" (Required Output: "role_ids"=>["2", "4"])
When I attempted using a loop:
<% for role in Role.find(:all) %>
<%= f.hidden_field :role_ids, :value => #user.roles.map(&:id).join(",").to_a %>
<% end %>
Yet another string resulted:
Parameters: {"commit"=>"Update", "action"=>"update", "_method"=>"put", "authenticity_token"=>"84Fvxxo3ao8Fl+aytOFInrLatUpslfAJggleEyy4wyI=", "id"=>"5", "controller"=>"users", "user"=>{"password_confirmation"=>"[FILTERED]", "role_ids"=>"2, 4, 5", "password"=>"[FILTERED]", "login"=>"lesa", "email"=>"lesa#gmail.com"}}
Any suggestions? This has been a perplexing problem.

If you want to submit an array of values I believe you can do something like this:
<% #user.roles.each do |role| %>
<%= f.hidden_field 'role_ids[]', :value => role.id %>
<% end %>
Rails should then merge the value of each hidden field with the same name 'role_ids[]' into an array giving you something like this:
Parameters: {"commit"=>"Update" ... "role_ids"=>["2", "4", "5"], ... }}

The accepted answer doesn't work for me. It works if you're using hidden_field_tag but not with f.hidden_field. I need to resort to
<%= f.hidden_field 'role_ids][', value: role.id %>
Note the reversed bracket.

if you pass just the u.roles.map(&:id)[0] what happens? is the value = 2? Maybe the value don't accept multivalue or maybe you should pass
<%= f.hidden_field :role_ids, :value=>“#{u.roles.map(&:id).join(',')}">

You could do the work in the controller edit action (air code):
#role_ids = #user.roles.map(&:id).join(";")
In the form:
<%= f.hidden_field, 'role_ids', :value => #role_ids %>
Then in the update action:
roles = params[:user][:role_ids].split(";")

Related

Is there some validation in Rails that edits a string before it commits to the database?

I'm getting an issue when submitting a form in a Rails app. There is no validation on this field in the model.
Here are the Params as they POST to the controller
"blast"=>{"to"=>"[947363, 947426, 947427, 947432, 947433]"}
Here is what is what is commited to the DB
["to", "{\" 947426\",\" 947427\",\" 947432\"}"]
EDIT:
Controller Code:
def update
if #blast.update(blast_params)
redirect_to URI.unescape(params[:ref]) if params[:ref].present?
end
end
private
def blast_params
params.require(:blast).permit(:subject, :to, :from_name, :from_email, :body, :json, :name, :active)
end
Model Code:
class Blast < ApplicationRecord
end
View:
<%= form_for Blast.new, url: blasts_create_path, html: {class: "validetta"} do |f| %>
<%= f.hidden_field :subject, value: blast.subject %>
<%= f.hidden_field :from_name, value: blast.from_name %>
<%= f.hidden_field :from_email, value: blast.from_email %>
<%= f.hidden_field :to, value: blast.to %>
<%= f.hidden_field :body, value: blast.body %>
<%= f.hidden_field :json, value: blast.json.to_s %>
<div class="form-group">
<%= f.text_field :name, value: "#{blast.name} (copy)", class: "form-control input-lg", data: {'validetta' => 'required'} %>
</div>
<div class="text-center">
<button type="submit" class="btn btn-md btn-success waves-effect waves-light" aria-expanded="false">Next Step <i data-feather="arrow-right-circle" width="15" height="15" style="vertical-align:middle;top:-1px;position:relative;" class="m-l-5"></i></button>
</div>
<% end %>
The :to field in the DB:
t.text "to", default: [], array: true
As you can see, it seems to cut the first and list items from the string. Has anyone had a similar issue?
The issue here was in respect to the serialisation of an array into a string as it is rendered on the DOM and then back into an array as it is passed as a paramater to the controller.
Update the controller as follows:
def create
blast = #account.blasts.create(blast_params)
if blast
if params[:blast][:to].present?
blast_to_params = params[:blast][:to].gsub('[', '').gsub(']', '').split(',').map{|i| i.to_i}
blast.update(to: blast_to_params)
end
redirect_to blasts_step_1_path(blast)
else
redirect_to blasts_path, alert: "Failed to create this blast."
end
end
array: true says to store the data as a Postgres array, but blast_params[:to] is formatted as JSON. Rails is jamming that JSON string in as a Postgres Array and something is getting lost in translation. Plus you're feeding integers to a text column, so it's hung on to the spaces.
[1] pry(main)> blast = Blast.new("to"=>"[947363, 947426, 947427, 947432, 947433]")
=> #<Blast:0x00007fa23091ff58
id: nil,
to: [" 947426", " 947427", " 947432"],
created_at: nil,
updated_at: nil>
You can fix this by first parsing the JSON to a Ruby Array. Then Rails will translate that to a Postgres Array. And you should switch the column type to integer. t.integer "to", default: [], array: true.
[1] pry(main)> blast = Blast.new("to"=>JSON.parse("[947363, 947426, 947427, 947432, 947433]"))
=> #<Blast:0x00007fa233ad0b38
id: nil,
to: [947363, 947426, 947427, 947432, 947433],
created_at: nil,
updated_at: nil>
Rather than using a Postgres array, I would suggest storing it as the more flexible jsonb: t.jsonb "to", default: []. You'll still have to parse the incoming JSON, but it will correctly accept a mix of text and integers.

Rails AssociationTypeMismatch on object sent by a check_box_tag inside a form_for

Rails is getting me this error Subject(#70287575068140) expected, got String(#70287576459120), but my parameters are:
Parameters:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"rISZVyQZgcLCGP1y7F4qg7xmW1miuaJvwUe5gu59/MqjjSWem/3JzCei5EPuZSSVdZyqO8bq0eRk0w9zCo0mDA==",
"student"=>{"name"=>"Eduardo Pedroso",
"rg"=>"39468291-0",
"phone"=>"981713271",
"address"=>"Rua dr Camilo marques",
"birthday"=>"2015-06-08",
"scholarity"=>"Superior",
"responsible_id"=>"1",
"subjects"=>["#<Subject:0x007fda346828e0>",
"#<Subject:0x007fda34682660>"]},
"commit"=>"Edit student",
"id"=>"1"}
And the checkbox that sends the subjects params
<% Subject.all.each do |subject| %>
<%= check_box_tag :subjects_id, subject, #student.subjects.include?(subject), :name => 'student[subjects][]' -%>
<%= label_tag :subjects, subject.name %>
<% end %>
Try changing
<%= check_box_tag :subjects_id, subject, #student.subjects.include?(subject), :name => 'student[subjects][]' -%>
to
<%= check_box_tag :subjects_id, subject, #student.subjects.include?(subject), :name => 'student[subjects_id][]' -%>
You better check the definitions around the check_box_tag helper
It goes like this:
check_box_tag(name, value = "1", checked = false, options = {})
So comparing with your use, you have assigned the name attribute twice and assigned as value the object subject.
I believe you intended to assign the subject.id as value, so you can fix it by replacing the check_box_tag call as:
<%= check_box_tag 'student[subjects][]', subject.id, #student.subjects.include?(subject) -%>

Generate parameters in ruby on rails

I want an update method in the controller to get parameters in a specific format. The current format by which I am getting the parameters is
Parameters: {"utf8"=>"✓", "authenticity_token"=>"temp", "performance_areas"=>{"0"=>{"performance_area"=>"New item"}, "1"=>{"performance_area"=>"Delete Item"}, "commit"=>"Submit"}
This is being generated as a name in my text field with 0 and 1 indicating the areas index. I want some additional parameters in this in the following format:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"temp", "performance_areas"=>{"0"=>{id: 1, description: "This is a test","performance_area"=>"New item"}, "1"=>{id: 2, description: "This is a test2","performance_area"=>"Delete Item"}, "commit"=>"Submit"}
Any pointers how to get the parameters in the way I want. I do have performance_areas that has the id and description in it. Do let me know if any further clarification is required.
EDIT: Form code (just pasting the critical section of the long code)
<%= form_tag({:performance => :update}, :method => :put) do %>
<% #performance_areas.each_with_index do |performance_area, i| %>
<%= hidden_field_tag :performance_area, performance_area, :name => "performance_areas[#{i}][performance_area]" %>
<%= submit_tag %>
I think you want this, I am assuming that both id and description are in performance_area object
<%= form_tag({:performance => :update}, :method => :put) do %>
<% #performance_areas.each_with_index do |performance_area, i| %>
<%= hidden_field_tag :id, performance_area.id, :name => "performance_areas[#{i}][id]" %>
<%= hidden_field_tag :description, performance_area.description, :name => "performance_areas[#{i}][description]" %>
<%= hidden_field_tag :performance_area, performance_area, :name => "performance_areas[#{i}][performance_area]" %>
<%= submit_tag %>
<% end %>

Rails nested form with simple form not showing nested objects' form

I've got a rails update form like this:
<%= debug #ballot.scores %>
<%= simple_form_for(#ballot, :url => "/vote/#{#election.id}/ballot/#{#ballot.position}/submit", :method => :post, :html => {:class => 'form-horizontal' }) do |f| %>
<% f.fields_for :scores do |builder| %>
<div class="slider">Slider goes here!</div>
<%= builder.hidden_field :initialValue, :value => 50 %>
<% end %>
<%= f.button :submit %>
<% end %>
The debug shows:
---
- !ruby/object:Score
attributes:
id: 1
ballot_id: 4
candidate_id: 4
initialScore:
normalizedScore:
created_at: 2013-08-08 05:00:10.391163000 Z
updated_at: 2013-08-08 05:00:10.432374000 Z
- !ruby/object:Score
attributes:
id: 2
ballot_id: 4
candidate_id: 5
initialScore:
normalizedScore:
created_at: 2013-08-08 05:00:10.418904000 Z
updated_at: 2013-08-08 05:00:10.434772000 Z
So there are definitely score objects, however no "Slider goes here!" text is showing up on the page. Why isn't this working?
Let me know if you need more info :)
You need to specify <%= fields_for ... you are missing the = sign.
Try:
<%= f.fields_for :scores do |builder| %>
Please refer to fields_for documentation on it's usage.
Although fields_for works regardless of use of simple_form, you could use simple_fields_for as well since you are using simple_form as follows:
<%= f.simple_fields_for :scores do |builder| %>

Passing an array into hidden_field ROR

I'm trying to pass an array into a hidden_field.
The following User has 3 roles [2,4,5]
>> u = User.find_by_login("lesa")
=> #<User id: 5, login: "lesa", email: "lesa.beaupry#gmail.com", crypted_password: "0f2776e68f1054a2678ad69a3b28e35ad9f42078", salt: "f02ef9e00d16f1b9f82dfcc488fdf96bf5aab4a8", created_at: "2009-12-29 15:15:51", updated_at: "2010-01-06 06:27:16", remember_token: nil, remember_token_expires_at: nil>
>> u.roles.map(&:id)
=> [2, 4, 5]
Users/edit.html.erb
<% form_for #user do |f| -%>
<%= f.hidden_field :role_ids, :value => #user.roles.map(&:id) %>
When I submit my edit form, I receive an error: ActiveRecord::RecordNotFound in UsersController#update "Couldn't find Role with ID=245"
How can I pass an array into the hidden_field?
I would use this technique.
<% #user.roles.each do |role| %>
<%= f.hidden_field :role_ids, :multiple => true, :value => role.id %>
<% end %>
:multiple adds both the [] to the end of the name attribute and multiple="multiple" to the input element, which is ideal. Rails uses this internally when it generates inputs for arrays, such as in fields_for.
Unfortunately it is not well-documented. I'm going to look into fixing this.
The only thing that works for me (Rails 3.1) is using hidden_field_tag:
<% #users.roles.each do |role| %>
<%= hidden_field_tag "user_role_ids[]", role.id %>
<% end %>
Try:
<% #user.roles.each_with_index do |role| %>
<%= f.hidden_field "role_ids[]", :value => role.id %>
<% end %>
using Rails 4.2.6
I was able to use
<% #user.roles.each do |role|
<%= f.hidden_field :role_ids, :id => role.id, :value => role.id, :multiple => true %>
<% end %>
which rendered:
<input id="12345" value="12345" multiple="multiple" type="hidden" name="role_form[role_ids][]">
trying to use hidden_field_tag or the 'role_ids[]' the param details were not being included in my form_params.
This didn't work:
<% #user.roles.each do |role|
<%= hidden_field_tag 'role_ids[]', role %>
<% end %>
because it renders:
<input type="hidden" name="role_ids[]" id="role_ids_" value="12345">
and is seen by the controller outside of the form params.
try with:
<%= f.hidden_field :role_ids, :value => #user.roles.map(&:id).join(", ") %>
edit: note - you'll need to do ids.split(", ") in your controller to convert them from a string into an array
I realize the original question involved a Rails form_for form, but I just encountered this problem while trying adding a hidden field to a form_tag form. The solution is slightly different.
The hidden_field_tag kept converting my ids to a space-separated string, like "1 2 3", not the array of ["1", "2", "3"] that I wanted. According to the docs, this is a known problem with hidden_field_tag. This is the approach that ended up working for my Rails 6 form_tag form:
In the view:
<%= hidden_field_tag :role_ids, my_role_ids_array %>
In the controller:
role_ids = params[:role_ids].split(' ').map(&:to_i)
Could solve this kind of issue with :
<% params[:filter].each do |filter| %>
<%= hidden_field_tag 'filter[]', filter %>
<% end %>

Resources