Rails 4 - Nested Attributes form only POSTing one field - ruby-on-rails

I'm new to Ruby on Rails, and I'm looking create a system where users join a house, and then can leave notes to other users in their house. Each note should have individual permissions per other users in their house. When a user creates a note, they will be greeted with a form with a text area for the Note content, and then a list of all the other users in the house, with a dropdown indicating that users level of permission on that note.
I'm attempting to use Partials to make things nicer, as Permissions are a polymorphic class and will be used in other parts of our website. Right now, when a Note is submitted as a new note, only one Permission is being POSTed, instead of a Permission for each user, e.g.
Started POST "/notes" for 127.0.0.1 at 2015-11-03 11:37:47 -0500
Processing by NotesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"8+fSG6oBsp4C3ikKc6mSNQYIB+mM5w3+42Y5tRVi5LOZY4a1lo0EApLo0WtSVNZ6/MO0yRwGitZzgZWaYR23mg==", "note"=>{"content"=>"okay...", "permissions_attributes"=>{"0"=>{"user_id"=>"0", "level"=>"3"}}}, "commit"=>"Post"}
I've spent a while comparing my View code with partials to others, and can't figure out why it's not submitting a permission for each user. Any advice? I can confirm that the permission that is submitted is created in the NoteController and created in the database correctly. It's just that instead of creating a permission for each user in the list, it's only creating the default permission.
Note Creation GUI
_form.html.erb
<%= form_for(#note) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new Note..." %>
</div>
<%= f.fields_for :permissions do |perm| %>
<%= render 'permission_new', :f => perm %>
<% end %>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
_permission_new.html.erb
<% House.find(current_user.relationship.house_id).users.each do |user| %>
<%= render 'permission', :f => f, user: user %>
<% end %>
<% User.new(id: 0) do |user| %>
<%= render 'permission', :f => f, user: user %>
<% end %>
_permission.html.erb
<% if current_user.id != user.id %>
<div class="permission">
<% if user.id == 0 %>
Default Permission
<% else %>
<%= user.name %>
<% end %>
<%= f.hidden_field :user_id, value: user.id %><br />
<%= f.select :level, [["Read Only", 3], ["Read, Edit, and Delete", 1],
["Read and Edit", 2], ["Can't Read", 4]] %>
</div>
<% end %>
note_controller.rb
...
def new
#note = Note.new
#note.permissions.build
end
def create
#note = current_user.notes.build(note_params)
if #note.save
#perm_user = #note.permissions.create(user_id: current_user.id, level: 0)
flash[:success] = "Note Created"
redirect_to notes_path
else
render 'new'
end
end
...
private
def note_params
params.require(:note).permit(:content, permissions_attributes: [:user_id, :level])
end

In case anyone happens to be having the same problem, I figured it out.
<%= f.fields_for :permissions do |perm| %>
<%= render 'permission_new', :f => perm %>
<% end %>
f.fields_for only technically creates one field per call. This means that in my _permission_new.html.erb file, where I have a loop creating a dropdown for each user in the house, and a dropdown for the default, all of those dropdowns were linked to the same field, and overwriting each other. Which is why only the last permission was being POSTed when a new Note was submitted. To fix this, you must call the f.fields_for multiple times in your form. In my case, I moved my f.fields_for into my _permission_new partial, and ended up with code like this.
_form.html.erb
...
<%= render 'permission_new', f: f %>
...
_permission_new.html.erb
<% House.find(current_user.relationship.house_id).users.each do |user| %>
<%= f.fields_for :permissions do |perm| %>
<%= render 'permission', :f => perm, user: user %>
<% end %>
<% end %>
<% User.new(id: 0) do |user| %>
<%= f.fields_for :permissions do |perm| %>
<%= render 'permission', :f => perm, user: user %>
<% end %>
<% end %>

Related

Remove array from showing under my rails erb-form

I have a checkbox form where you can select many customers to attend a single event. The form works but there is an array of all customers under it and I can't figure out how to remove it.
events.rb
def addcustomer
#event = Event.find(params[:id])
#customer = Customer.all
end
routes.rb
resources :events do
get 'addcustomer', on: :member, as: 'add'
end
addcustomerform.html.erb
<%= form_for(#event) do |f| %>
<%= hidden_field_tag "event[customer_ids][]", nil%>
<%= #customer.each do |customer| %>
<%= check_box_tag "event[customer_ids][]", customer.id,
#event.customer_ids.include?(customer.id), id:dom_id(customer) %>
<%= label_tag dom_id(customer), customer.id %>
<%= label_tag dom_id(customer), customer.name %> --
<%= label_tag dom_id(customer), customer.email %> --
<%= label_tag dom_id(customer), customer.phone %>
<br>
<% end %>
<br>
<%= f.submit%>
<% end %>
Here is a photo of what the issue looks like:
Here is the repo
https://github.com/robbiesoho/fanfactory
I hope someone can help. Thank you
it is not the params that are shown there but all the customers.
If you replace <%= #customer.each do |customer| %> with <% #customer.each do |customer| %>
The difference is that I remove the =. The = means that the line should be added to the HTML as text. on that line is the #customer array, and the result f #customer.to_s is what you see there.
For more information, please read: What is the difference between <%, <%=, <%# and -%> in ERB in Rails?

Pass new parent object id to link_to

I am trying to save parent as well as child object at the same time using accepts_nested_attributes_for
Code in controller's new method:
def new
#project = Project.new
#project.instances.build
end
and form looks like this:
<%= simple_form_for(#project) do |f| %>
<%= f.input :name %>
<%= link_to "Add New Instance", new_project_instance_path(#project), id: "new_link", remote: true %>
<% end %>
The route entry for this is:
resources :projects do
resources :instances
end
And the fields that need to be displayed instances/_form.html.erb:
<%= form.simple_fields_for :instances do |i| %>
<%= i.input :user_id %>
<%= i.input :password %>
<%= i.input :service_url %>
<% end %>
The issue here project_id being :nil, it is giving error:
No route matches {:action=>"new", :controller=>"instances", :project_id=>nil} missing required keys: [:project_id]
I need to somehow call <%= render 'cdd_instances/form', form: f %>, so that the fields get rendered below the Project details, how should I implement this?
I think your #project is null you have to pass like:
new_project_instance_path(project_id: (#project || ''))
In this case you are not able to pass non-persisted #project to create this link_to url.
I believe you are looking for something like: cocoon.
<%= simple_form_for #project do |f| %>
<%= f.input :name %>
<h3>Instances</h3>
<div id="instances">
<%= f.simple_fields_for :instances do |instance| %>
<%= render 'instance_fields', f: instance %>
<% end %>
<div class="links">
<%= link_to_add_association 'add instance', f, :instances %>
</div>
</div>
<%= f.submit %>
<% end %>
Cheers!

Rails - Conditional statement in form recognizing wrong url paramater

I have a polymorphic relationship between Attachments and Users & Teams so that both users and teams can upload attachments. The :team_id or :user_id is passed along as a param when the "Add file" button is clicked. Then, a hidden field in the attachments#new form tells the controller if the request is coming from a Team or User so that attribute_update can be applied to the proper model. In practice, however, the team request is being delivered as user subaction and vice versa. Any ideas as to what may be going wrong?
class Attachment < ActiveRecord::Base
belongs_to :upload, polymorphic: true
end
class User < ActiveRecord::Base
has_many :attachments, as: :upload
end
class Team < ActiveRecord::Base
has_many :attachments, as: :upload
end
team#show
<%= link_to "Add Files", new_attachment_path(:team_id => #team.id), class: "btn btn-md" %>
user#show
<%= link_to "Add Files", new_attachment_path(:user_id => current_user), class: "btn btn-md" %>
attachments#new
<% provide(:title, 'Add file') %>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#attachment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.file_field :file %>
<% if request.path == new_attachment_path(params[:team_id]) %>
<%= hidden_field_tag(:subaction, 'Team') %>
<% elsif request.path == new_attachment_path(params[:user_id]) %>
<%= hidden_field_tag(:subaction, 'User') %>
<% end %>
<%= f.submit "Add Files", class: "btn btn-primary" %>
<% end %>
</div>
</div>
attachment controller
def create
#attachment = Attachment.create(attachment_params)
#team = Team.find_by(params[:team_id])
#user = User.find_by(params[:user_id])
if #attachment.save
if params[:subaction] == 'Team'
#attachment.update_attribute(:upload, #team)
flash[:success] = "Team file uploaded!"
redirect_to #team
elsif params[:subaction] == 'User'
#attachment.update_attribute(:upload, #user)
flash[:success] = "User file uploaded!"
redirect_to current_user
end
else
render 'new'
end
end
I think the problem is with your if statement in attachments#new. Both params[:team_id] and params[:user_id] would return an integer so your if statement can't really tell if the request is coming from a team or a user. As a suggestion you could store your params[:team_id] in a variable such as #team_id and then change your if statement to something like this:
<% unless #team_id.nil? %>
<%= hidden_field_tag(:subaction, 'Team') %>
<% else%>
<%= hidden_field_tag(:subaction, 'User') %>
<% end %>
However you may be able to improve this further by using polymorphic associations in your models as outlined in the following link: https://launchschool.com/blog/understanding-polymorphic-associations-in-rails
Changed the url param to pass the string 'User' or 'Team' instead of the team_id or user_id allowing the controller if statement to function properly.
Team#show
<%= link_to "Add Files", new_attachment_path(:upload_type => 'Team'), class: "btn btn-md" %>
User#show
<%= link_to "Add Files", new_attachment_path(:upload_type => 'User'), class: "btn btn-md" %>
Attachments#new
<% provide(:title, 'Add file') %>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#attachment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.file_field :file %>
<%= hidden_field_tag(:subaction, params[:upload_type]) %>
<%= f.submit "Add Files", class: "btn btn-primary" %>
<% end %>
</div>
</div>

Ruby On Rails - creating an object does not respect validations and showing error messages

I'm having troubles handling objects that not respect the validation.
I'm building an app in which an user can create a "Trip" model and then add steps to his trip, that I called "Traces". Each added trace prints a new part of a map present in the trip#show action.
The association between models is user has_many trips and trip has_many traces
In the users#show I put a "CREATE NEW TRIP" button linking to the trips#new and here I have the form_for with the field corresponding to the Trip attributes.
When I fill the form correctly everything is ok. When something is missing or wrong (for the validations) I get this error:
NoMethodError in Trips#create
undefined method `model_name' for Array:Class
------ in the trips_controller.rb
def create
#trip = current_user.trips.build(params[:trip])
if #trip.save
# handle a successful save
flash[:success] = 'Trip created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trip = []
#feed_items = []
render 'new'
end
end
------ in app/views/trip, in the new.html.erb
h1>Create a trip</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3 general-input">
<%= form_for ([current_user, #trip]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name,'Give a name to your trip ;)' %>
<%= f.text_field :name %>
<%= f.label :trip_start, 'Choose your starting point!' %>
<%= f.text_field :trip_start %>
<%= f.label :departure, 'When are you leaving?' %>
<%= f.date_select :departure, :start_year => Date.current.year %>
<%= f.label :arrive, 'And coming back home?' %>
<%= f.date_select :arrive, :start_year => Date.current.year %>
<%= f.submit 'Create a new trip', class: 'btn btn-primary btn-lg' %>
<% end %>
EDIT 1: problem solving removing #trace=[ ] from trips_controller.rb
EDIT 2:
I also have a similar problem with the creation of a new Trace:
The form for adding a new trace is in the trip#show page.
When I try to create a trace that not respects the validation (e.g. if I leave blank the "destination" field) I get this error:
NoMethodError in Posts#create
undefined method `any?' for nil:NilClass
When I'm on the Trip page where the form for the Traces is placed, the URL is like:
http://localhost:3000/users/2/trips/8
but when I create a not valide Trace it switchs to a path like:
http://localhost:3000/trips/8/posts
I suppose I'm doing something wrong handling the error messages. I probably misunderstood something, even because I'm new to Rails and web programming in general.
Here you are some code parts, hoping it helps to understand my mistake:
------ in the traces_controller.rb
def create
#trip= Trip.find(params[:trip_id])
#trace = #trip.traces.create(params[:trace])
if #trace.save
flash[:success] = 'Trace created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trace=[]
render 'trips/show'
end
end
------ in app/views/shared, in the add_trace_form.html.erb
<p>Complete your trip adding a route!</p>
<%= form_for ([#trip, #trip.traces.build]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="block post-form">
<div class="field ">
<%= f.text_field :end, placeholder: 'Where are you going next?' %>
<%= f.label :arr_day, 'When?' %>
<%= f.date_select :arr_day, :start_year => Date.current.year %>
<%= f.submit 'Add route', class: 'btn btn-primary btn-landing' %>
</div>
</div>
<% end %>
------ in app/views/shared, in the error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |message| %>
<li>* <%= message %></li>
<% end %>
</ul>
</div>
<% end %>
------ in the routes.rb
resources :users do
resources :trips
end
resources :trips do
resources :traces
end
resources :traces
Thanks a lot
i think when you are passing the f.object in locales in render its is passing array not the active record object ,<%= render 'shared/error_messages', object: f.object %>.
Can u check inspecting object in your partial and what class it has.
Try inspecting object.errors.inspect
Try refering http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Limit User to 1 Like/Dislike per Post

I have a listing model (which allows comments) and users can like (thumbs up) or dislike (thumbs down) the listing. It works at the moment but I want to iterate over the likes for a specific listing, and if any like's user_id listing.likes.user_id matches the current users ID current_user.id then remove the form to like (users can add a reason why they are liking the listing)
<%= form_for([#listing, #listing.likes.build]) do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
I have something halfway there that checks if the current likes user_id matches the current user id, if so provide a link to delete (remove/unlike) the like.
<% if current_user.id == like.user_id %>
<%= link_to '[ Delete Like ]', [like.listing, like],
method: :delete,
data: { confirm: 'Are you sure?' } %>
<% end %>
How would I go about using the code to remove the like form if the user has already created a like/if the user's ID matches the user_id of a like created for a specific listing(something like this?)
<% if current_user.id == listing.likes.any.user_id %>
<% else %>
<%= form_for([#listing, #listing.likes.build]) do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
<% end %>
I think you had the right idea with the any method, but were not using it correctly. any can accept a block in which you can do your user_id comparison, like so:
<% if listing.likes.any{|like| like.user_id == current_user.id } %>
In this case, any will return true the instant the condition in the block evaluates to true.
Let me know whether this works.
Side Note
If my method works for you, you may like to further clean up your view code by moving that check in to the Listing model as a helper method. Something like this:
class Listing < ActiveRecord::Base
# ... other stuff
def has_comment_from?(target_user)
likes.any{|like| like.user_id == target_user.id }
end
end
And then you can simply call it from the view like so:
<% if listing.has_comment_from? current_user %>
Found a partial solution but it stops the user from commenting after 1 comment, not 1 comment per listing.
<% #listing.likes.each do |like| %>
<% if current_user.id == like.user_id %>
<% else %>
<%= form_for([#listing, #listing.likes.build]) do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>

Resources