Why does variable pass on create, but not on destroy? - ruby-on-rails

I have a form on the chords/:id/show page (shown below). I am able to input a :note_id into the form, which then creates a ChordNote using the :chord_id from :params and the :note_id from the form. This works well. However, when I try to delete a ChordNote using the same form, I get an error that says:
NoMethodError in ChordNotesController#destroy, undefined method '[]' for nil:nilClass
This is the controller for 'ChordNote', which joins chords and notes in a many-to-many relationship.
def create
#chord = Chord.find(params[:chordnote][:chord_id])
#note = Note.find(params[:chordnote][:note_id])
if #chord.hasnote?(#note)
# Add error message here, have it not redirect
redirect_to #chord
else
#chord.addnote!(#note)
redirect_to #chord
end
end
def destroy
#chord = Chord.find(params[:chordnote][:chord_id])
#note = Note.find(params[:chordnote][:note_id])
Chordnote.find_by(note_id: #note.id, chord_id: #chord.id).destroy
redirect_to chord_path(#chord)
end
This is the form (that appears on chords/:id/show):
<%= form_for(#chord.chordnotes.build(chord_id: #chord.id)) do |f| %>
<div><%= f.hidden_field :chord_id, value: #chord.id %></div>
<div class="field">
<%= f.text_field :note_id, placeholder: "Enter the note's id" %>
</div>
<%= f.submit "Add Note", class: "btn btn-large" %>
<%= link_to "Remove Note", Chordnote.find_by(note_id: 1), method: :delete, title: "test title", class: "btn btn-large" %>
<% end %>
Any thoughts on why destroy is not working? Thanks!
undefined method `[]' for nil:NilClass
def destroy
####The error is on the following line####
#chord = Chord.find(params[:chordnote][:chord_id])
#note = Note.find(params[:chordnote][:note_id])
Chordnote.find_by(note_id: #note.id, chord_id: #chord.id).destroy
redirect_to chord_path(#chord)
Rails.root: /Users/mydocs/myprojects/rails_projects/what_key_v002
Application Trace | Framework Trace | Full Trace
app/controllers/chordnotes_controller.rb:23:in `destroy'
Request
Posted parameters:
{"_method"=>"delete",
"id"=>"10"}

In your destroy action, you're trying to make a lookup off params[:chordnote][:note_id], but the only available parameters are {"_method"=>"delete", "id"=>"10"}. You need to add both note_id and chord_id as arguments in your link_to helper:
<%= link_to "Remove Note", chordnote_path(:chord_id => #chord.id, :note_id => 1), :method => :delete %>
# => Remove Note
Then, in your destroy action, make your lookups off params[:chord_id] and params[:note_id]:
def destroy
Chordnote.find_by(note_id: params[:note_id], chord_id: params[:chord_id]).destroy
redirect_to chord_path(params[:chord_id])
end

Chordnote.find_by(note_id: #note.id, chord_id: #chord.id)
cant find a record, so its nil and nil doesnt have a method called destroy.
Are you sure you are passing the correct params to the action?
Update:
params[:chordnote] #seems to be nil, so
params[:chordnote][:note_id] => exception
Check the params which are posted to the action. You can see it in your console logs.
Update:
Your link should maybe be something like this:
<%= link_to "Remove Note", chord_notes_path(note_id: 1, chord_id: #chord.id), method: :delete, title: "test title", class: "btn btn-large" %>
And in your delete action
#chord = Chord.find(params[:chord_id])
#note = Note.find(params[:note_id])

Related

Creating new model instance with paramethers (Rails)

I need to create new Event from User's guest page and the Event.visitor_id should be User.id
Event.rb
def create
#event = current_user.owner_events.new(event_params)
end
protected
def event_params
params.require(:event).permit(:visitor_id)
end
I need to correct the view below which is not working:
<%= link_to "Create event with this user", events_path(visitor_id: #user.id), method: :post %>
I get: ActionController::ParameterMissing in EventsController#create
Try that (you forgot to add event key):
<%= link_to "Create event with this user", events_path(event: { visitor_id: #user.id }), method: :post %>
For more details read 'strong parameters' gem documentation.
I just had to add .to_i to #user.id
<%= link_to "Create event", events_path(:event =>{:visitor_id => #user.id.to_i} ), :method => :post %>

Updating a rails attribute through a model association

I have two models shop and user. Shop has_many Users and User has_one Shop. I'm trying to create a button on shop/show that allows a user to choose its shop. The button should send the parameter shop_id to Users_Controller#update_shop which then changes User.shop_id to the id of the shop on the page.
The current method in Users_Controller#update_shop is the following:
def update_shop
#user = User.find(params[:id])
#user.update_attributes(shop_id: params[:shop][:id])
flash[:success] = "Added Shop!"
end
And the button on show/shop is this:
<%= form_for User.update_shop, remote: true do |f| %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.submit 'Add As Your Shop', :class => 'button blue-button' %>
<% end %>
I'm getting the error NoMethodError in ShopsController#show undefined methodupdate_shop' for #`. The method is defined in the Users controller though.
I understand there is probably a more efficient way to update User.shop_id through the association, so tips on doing that or getting this to work are greatly appreciated.
I think it's better to have a link with POST method in place of form:
<%= link_to 'Add As Your Shop', update_shop_url(id: #user.id, shop_id: #shop.id, class: 'button blue-button', data: { method: 'post' } %>
With this setup you should change controller method as well:
def update_shop
...
#user.update_attributes(shop_id: params[:shop_id])
...
end

How to pass an argument into a controller method from a form_tag

In my User Show view:
<%= form_tag assign_as_student_path do %>
<%= hidden_field_tag :user_id, #user.id %>
<%= submit_tag "Assign as a Student", data: { confirm: "Are you sure?" } %>
<% end %>
TeachersController (inherits from UsersController):
def assign_as_student
#user = User.find(params[:user_id])
#user.update_attributes(type: "Student")
#user.save
redirect_to admin_path, notice: "They are now a Student"
end
routes.rb:
post 'assign_as_student' => 'teachers#assign_as_student'
Ok, this WORKS. But...
This was what I had to settle for, because I didn't know how to pass an argument into the controller method.
I wanted to write a method in the UsersController like the above one but with an argument like this (I think this would work, but haven't been able to test it):
def assign_as_type(type)
#user = User.find(params[:user_id])
#user.update_attributes(type: type)
#user.save
redirect_to admin_path, notice: "They are now a Student"
end
This way, I would be able to just have this one method instead of having an "assign_as_student" method AND an "assign_as_teacher" method. Always good to be DRY.
So... I think the above method would work, but how would I pass the argument in from the form? Like the below form, but with something added that would pass in a string "Student" or "Teacher":
<%= form_tag assign_as_type_path do %>
<%= hidden_field_tag :user_id, #user.id %>
<%= submit_tag "Assign as a (Student/Teacher), data: { confirm: "Are you sure?" } %>
<% end %>
(In this answer I assume you are using Rails 4+, otherwise some of my suggestions might not apply, it is good to supply this in the question.)
To pass another argument into the controller you could do:
<%= form_tag assign_as_type_path do %>
<%= hidden_field_tag :user_id, #user.id %>
<%= select_tag :type, options_for_select([ "Student", "Teacher" ]) %>
<%= submit_tag "Assign", data: { confirm: "Are you sure?" } %>
<% end %>
The parameter should then be accessible as params[:type].
Since this is an action on a #user-object I think you should do like this:
<%= form_for #user, url: assign_as_type_path do |f[ %>
<%= f.select :type, options_for_select([ "Student", "Teacher" ],#user.type) %>
<%= f.submit "Assign", data: { confirm: "Are you sure?" } %>
<% end %>
And also in your controller I think you should do like this:
(Here assuming that you changed to form_for)
def assign_as_type(type)
#user = User.find(params[:id])
if #user.update(type: params[:user][:type]) # This sets attribute + saves
redirect_to admin_path, notice: "They are now a " + #user.type
else
redirect_to admin_path, error: "User could not be updated."
end
end
This way, you can use model validation and if you do the redirect as in a normal update action you can also show errors in the form.
Hope this helps, if there is any questions, just ask 😄
Updated answer with params
The way I understood params (and understand if something is wrong) is by looking at your latest request in the Server log (if you are running it locally it is where you started rails s).
Here you would see:
params: { user_id: 1, type: "Student" , commit: "Assign"}
and some other extra parameters.
This is a normal Ruby Hash called params. To get into the Ruby-language you can do the: http://tryruby.org/ tutorial 😄
If you change it to form_for your params hash will look like:
params: { user: { id: 1, type: "Student" }, commit: "Assign" }
Ref:
http://ruby-doc.org/core-2.2.0/Hash.html

Adding a create comment page

Rails beginner here. I'm having trouble with commenting on posts. I have the comments working when they are on the same page as the post but after trying to set up seperate pages for creating and viewing the comments i get the following error:
No route matches {:action=>"new", :controller=>"comments", :id=>"27"} missing required
keys: [:post_id]
My posts/show.html.erb file: (the second line is the link that's causing the problem)
<h class="eventhead"><%= #post.description %></h>
<%= link_to "Add comment", new_post_comment_path, class: "btn btn-primary btn-medium" %>
And here's my comments/_form.html.erb file:
<%= simple_form_for [#post, Comment.new] do |f| %>
<p>
<%= f.input :title, :subtitle, :body, :label => "New Comment", as: :text, input_html: {
rows: "3" } %>
</p>
<p><%= f.submit "Add Comment", class: "btn btn-primary" %></p>
<% end %>
Thanks for the help!
You are using a nested resource, so this requires that you pass in the post_id to the link_to. Revise your code as:
<%= link_to "Add comment", new_post_comment_path(#post), class: "btn btn-primary btn-medium" %>
Just as #jaycode mentioned you will need to make sure the comment#new action assigns #postin your controller.
# comments_controller.rb
def new
#post = Post.find params[:post_id]
#comment = Comment.new
end
Welcome to Rails :)
Check the content of method new_post_comment_path. Most likely it requires :post_id variable to be passed into it.
Perhaps you should use this instead:
And make sure in comments_controller.rb action new (def new in that file), you have #post_id variable defined.

Rails Button to Update Attribute Not Working

I have a link that looks like this in the object's show view:
<%= link_to "Approve", object_path(#object, status: true), method: :put, confirm: "Are you sure?", class: 'button' %>
The update action in the controller is standard and the object's regular edit form works just fine. But for some reason, this link isn't updating the boolean field to true...
Is there something that I did wrong with this link?
EDIT: Update Section of Controller
def update
#object = Object.find_by_certain_field(params[:id])
if #object.update_attributes(params[:object])
flash[:success] = "Object Updated"
redirect_to #object
else
render 'edit'
end
end
object_path(#object, status: true) causes params[:status] to be true and not params[:object][:status] as you wish, thus not updating the object with #object.update_attributes(params[:object])
If you want to keep using the current code pattern just add
parmas[:object] ||= {}
params[:object][:status] = params[:status] if params[:status]
before
if #object.update_attributes(params[:object])
and everything should be fine.
Otherwise I'd avise you to make a form for this update call:
<% form_for #object, :method => :put do |f| %>
<%= f.hidden_field :status, :value => true %>
<%= f.submit "Approve" %>
<% end %>

Resources