Rails Linked Models undefined method for nil:NilClass - ruby-on-rails

I've been following this, making modifications where necessary.
I have equivalents of #post and #comment. My comments are 'questions'. I've tried following this procedure again but to the comments (rather than posts). Comments on comments if you like, these are answers/responses to the questions.
I've not been successful in getting this to work.
undefined method 'answers' for nil:NilClass`
is the error in answers/_form.html.erb which is rendered as part of a comment, which is in turn rendered as part of my post equivalent.
<%= form_for([#question, #question.answers.build]) do |f| %> is the line that raises the exception. I'm thinking the problem is that #question isn't being given a value for some reason?
Either that or my routes file isn't right (would be my guess at least).
resources :posts-equivalent do
...
resources :questions
end
resources :questions do
resources :answers
end
This has had me stumped all day - any ideas would be appreciated. If you'd like to see any more code drop a comment and I'll update it here.
Additional Code:
Error Message:
Showing .../app/views/answers/_form.html.erb where line #1 raised:
undefined method `answers' for nil:NilClass
Extracted source (around line #1):
1: <%= form_for([#question, #question.answers.build]) do |f| %>
Answers Controller:
class AnswersController < ApplicationController
def create
#question = Question.find(params[:question_id])
#answer = #question.answers.create(params[:answer])
redirect_to concept_path(#question.concept)
end
end

It looks like your AnswersController is not assigning a value to the #question instance variable. It's not clear what action you are running, but I'm assuming it's either new or edit based on the fact that you have a form. You should have something like this in those actions:
class AnswersController < ApplicationController
def new
#question = Question.find(params[:question_id])
#answer = #question.answers.new
end
def edit
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
end
end
Then you can have a single form shared between new and edit:
<%= form_for([#question, #answer]) %>

In the end I sorted this in a different manner. Each question was only ever going to have one answer and so I just gave question an answer attribute and assigned that.
On the post page:
...
<%= render #questions %>
<%= render "questions/form" %>
...
In _form.html.erb
<%= form_for([#concept, #concept.question.build]) do |f| %>
<%= f.hidden_field :contributor_id, :value => session[:contributor_id] %>
<div class="field">
<%= f.text_field :question, :placeholder => "Follow-up Question" %>
</div>
<% end %>
In the _question.html.erb
<%= form_for(question) do |f| %>
<div id="question">
"<%= question.question %>" asked by <%= question.contributor.email %> | <%= question.created_at %>.
<div id="answer">
- Answer: <%= question.answer %>
<%= f.text_field :answer, :placeholder => "Respond..." %>
</div>
</div>
<% end %>
Add this to the questions controller:
def update
#question = Question.find(params[:id])
#question.update_attributes(params[:question])
#concept = Concept.find(#question.concept.id)
redirect_to concept_path(#concept)
end
And that worked for me, I know it's not what I originally asked but that fixed my problem. Thanks to those who did try to help.

Related

How do I get a destroy method to work for nested resources in Rails?

Currently learning Ruby on Rails and creating a simple blog app with comments. I have a Comment model and an Article model. Comment is polymorphic and both models have many comments.
I'm trying to come up with a destroy method that's able to delete both the comments that belong to Comment and the ones that belong to Article (and that remain as [deleted] without destroying their children, much like in Reddit, although I haven't even gotten to that part).
I have tried different paths but I haven't got it right yet. Nested paths still confuse me a little and I'm not sure on how to pass the params that the path requests when creating the link_to.
These are my files:
routes.rb:
Rails.application.routes.draw do
get 'comments/new'
get 'comments/create'
get 'articles/index'
get 'articles/show'
root 'articles#index'
resources :articles do
resources :comments
end
resources :comments do
resources :comments
end
end
article.rb:
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
comment.rb:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: :true
has_many :comments, as: :commentable
end
comments_controller.rb:
class CommentsController < ApplicationController
before_action :find_commentable
def new
#comment = Comment.new
end
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
redirect_back(fallback_location: root_path)
else
redirect_back(fallback_location: root_path)
end
end
def destroy
#comment = #commentable.comments.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
private
def comment_params
params.require(:comment).permit(:body)
end
def find_commentable
if params[:article_id]
#commentable = Article.find_by_id(params[:article_id])
elsif params[:comment_id]
#commentable = Comment.find_by_id(params[:comment_id])
end
end
end
show.html.erb, where the form for commments that belong to Article.rb is:
<h1> <%= #article.title %> </h1>
<p> <%= #article.body %> </p>
<small>Submitted <%= time_ago_in_words(#article.created_at) %> ago </small> <br/>
<h3>Comments</h3>
<%= form_for [#article, Comment.new] do |f| %>
<%= f.text_area :body, placeholder: "Say something!" %> <br/>
<%= f.submit "Submit" %>
<% end %>
<ul class="parent-comment">
<%= render partial: 'comments/comment', collection: #article.comments %>
</ul>
<%= link_to "Index", articles_path %>
And the partial _comment.html.erb , which displays the comments that belong to the article as well as those that belong to other comments, and where I'm trying to integrate the link_to:
<p> <%= comment.body %> </p>
<small>Submitted <%= time_ago_in_words(comment.created_at) %> ago </small> <br/>
<%= form_for [comment, Comment.new] do |f| %>
<%= f.text_area :body, placeholder: "Add a reply!" %><br/>
<%= f.submit "Reply" %>
<%= link_to "Delete", comment_path(comment), method: :delete %>
<% end %>
<ul>
<%= render partial: 'comments/comment', collection: comment.comments %>
</ul>
Whenever I do seem to get the path right, NoMethodError in CommentsController#destroy — undefined method `comments' for nil:NilClass comes up. Why would the controller show it as undefined? It worked in the new method, as far as I can see.
Could you give some guidance as to what I should do or what I should fix? I'm not sure how to delete the parent comments, either, and I haven't managed to find information that suits this case. If you know where to point me to, I'm all eyes.
Thank you.
Because of your design model structure.
Your view
<%= link_to "Delete", comment_path(comment), method: :delete %>
Your find_commentable
elsif params[:comment_id]
#commentable = Comment.find_by_id(params[:comment_id])
end
#commentable will be a Comment class, so it won't have .comments methods as your Article class
check carefully to destroy the method
def destroy
#comment = #commentable.comments.find(params[:id])
#comment.destroy
redirect_back(fallback_location: root_path)
end
use #comment = #commentable.comments.find_by(id: params[:id]) and check whether #comment has some value or not?
just add one condition like this and it won't throw the error:
#comment.destroy if #comment
if #comment is nil and trying to destroy then it will throw the error.

Error: param is missing or the value is empty: thing

I'm using rails 4.0.8. I added a comment section to a model called 'Things', but I keep getting the same error "param is missing or the value is empty: thing" when I press the submit comment button. It says the error is in the Things#Controller. What am I doing wrong?
UPDATE: I removed the url path from the form, but a new error returns "Couldn't find Thing without an ID". The error is in Comments#Controller.
VIEW FOR THING/SHOW
<div id= "thing">
<h1>
<%= #thing.name %>
</h1>
<br>
<div id= "commentsection">
Comments
<div id= "comments">
<br>
<% #thing.comments.each do |c| %>
<%= c.username %>
<br>
<%= c.text %>
<% end %>
<%= form_for #comment, :url => thing_path do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :comment %>
<%= f.text_field :text %>
<%= f.submit "Enter", class: "btn btn-small btn-primary" %>
<% end %>
</div>
</div>
</div>
THINGS CONTROLLER
class ThingsController < ApplicationController
def show
#thing = Thing.find(params[:id])
#thing.comments.build
#comment = Comment.new
end
def index
end
def new
#thing = Thing.new
#things = Thing.all
end
def create
#thing = Thing.new(thing_params)
if #thing.save
redirect_to #thing
else
render 'new'
end
end
private
def thing_params
params.require(:thing).permit(:name, :avatar)
end
end
COMMENTS CONTROLLER (I put asterisks around the line where the error is)
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
#comments = Comment.all
end
def create
****#thing = Thing.find(params[:thing_id])****
#comment = #thing.comments.create(comment_params)
redirect_to thing_path(#thing)
end
end
private
def comment_params
params.require(:comment).permit(:user, :text, :upvotes, :downvotes, :thing_id)
end
end
ROUTES
Website::Application.routes.draw do
get "comments/new"
get "comments/show"
get "things/new"
root 'home_page#home'
get "all/things/new" => 'things#new'
get "all/allthings"
resources :things
resources :good_comments
get "things/show"
get "things/results"
end
You are posting the #comment form to post '/things' path.
<%= form_for #comment, :url => thing_path do |f| %>
It should just be <%= form_for #comment do %> (Rails is smart enough to plug in the comments_path) or if you feel like being more explicit (even though it's not necessary)
<%= form_for #comment, url: :comments_path do %>
Another note though, if you want that Comment to be tied to that specific Thing then in your models it should be
Class Thing
has_many :comments
end
Class Comment
belongs_to :thing
end
Then make sure in your database comment has a thing_id foreign_key field and then your form for comment should actually look like
<%= form_for #thing, #comment do %>
<% end %>

Form resulting in blank post with no ID

I am new to Rails and working on creating a generic "facebook" type of app as practice with users and posts associated with each user. However, I'm currently having an issue where I think the form that I am using to create the posts is also being rendered out as a blank post with no post ID where I display all of the posts in a section below. I think that this post is being shown even before it is being saved to the database.
Here is my code in my view:
<div class="newpostcontainer">
<div class="newposttext">
<%= form_for([#user, #user.posts.build]) do |f| %>
<%= f.text_area :post, size: "69x1" %>
</div>
<div class="newpostsubmitbutton">
<%= f.submit %>
</div>
<% end %>
</div>
<% #user.posts.reverse_each do |p| %>
<div class="postedcontainer">
<div class="minipostpic">
<%= image_tag #user.photo.url, width: 32, height: 32 %>
</div>
<div class="nameofposter"><%= #user.name %></div>
<div class="dateofpost"><%= p.created_at%></div>
<div class="postcontent"><%= p.id%></div> <br>
<div class="postcontent"><%= p.post%></div> <br>
<div class="likecommentdelete">
<%= link_to "Delete", [p.user, p], method: :delete %> | Like | Comment
</div>
</div>
<%end%>
</div>
Here is my controller:
def index
#user = User.find(params[:user_id])
#posts = #user.posts.all
end
def create
#user = User.find(params[:user_id])
#post = #user.posts.create!(post_params)
redirect_to user_path(#user)
end
def show
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#post = #user.posts.find(params[:id])
#post.destroy
if #post.destroy
redirect_to user_path(#user)
else
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit!
end
end
And here is my model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates_presence_of :post
end
I'm pretty sure the issue has something to do with my form to create the new post because when I remove it or comment it out, the extra blank post with no post ID goes away.
Any thoughts or suggestions?
Thank you!!
I think you need to permit the field values to be posted:
i.e.,
params.require(:post).permit!
should be
params.require(:post).permit(:name, :post)
then only it will POST I think.
Hope it helps :)
This is because of rails 4 strong parameter feature. You need to whitelist your active models parameters. For more details refer to here.
In your case you need to do something like this:
params.require(:post).permit(:post)
where the ":post" inside require is your model and the other one is your permitted field that is your textarea.
Several issues -
Form
<%= form_for([#user, #user.posts.build]) do |f| %>
Why are you building an associative object? #user.posts.build will not persist your data, and will cause all sorts of non-conventional issues I would highly recommending building the posts associative object in your controller's new action before using in the view, so you can do this:
#app/controllers/users_controller.rb
def new
#user = current_user
#user.posts.build
end
<%= form_for #user do |f| %>
Association
You're trying to edit the post attribute with this statement:
<%= f.text_area :post, size: "69x1" %>
This won't work in any circumstance, as :post is an association, not an object. Rails only allows you to change / add attributes to specific objects, which means you'll be better doing something like this:
<%= f.fields_for :posts do |p| %>
<%= p.text_area :title %>
<%= p.text_area :body %>
<% end %>
Strong Params
You're currently permitting all your params? You'll be better doing this:
def post_params
params.require(:user).permit(posts_attributes: [:title, :body])
end
Use Posts Controller
A better way will be to just use the posts_controller, like this:
#app/controllers/posts_controller.rb
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.save
end
#app/views/posts/new.html.erb
<%= form_for #post do |f| %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<% end %>

Error during the creation of hidden fields for nested objects

I am using rails 2.3.5. I have a Blog model and Blog has many comments. This is my Blog controller show action
def show
#blog = Blog.find(params[:id])
#comment = Comment.new
end
I would display the Blog and at the end would have an option for creating comment. So I add this in blogs/show.html.erb.
<% form_remote_for #comment do |f| %>
<%= f.label :content %>
<%= f.text_area :content, :rows => 6 %>
<%= f.hidden_field :blog => #blog %>
<%= f.submit %>
<% end %>
But i get the following error when i run this
NoMethodError in Blogs#show
Showing app/views/blogs/show.html.erb where line #270 raised:
undefined method `blog#<Blog:0xb677d8d0>' for #<Comment:0xb67762b0>
Extracted source (around line #270):
Comment model should have belongs_to :blog
Blog model should have has_many :comments
Initialize the comment in the controller like this:
#blog.comments.new
The view should be like this:
<%= f.hidden_field :blog_id %>
you have to hide the id of the blog not the blog object.
<%= f.hidden_field :blog_id%>
The problem is your f.hidden_field line. The first parameter should be the attribute name of the #comment you want in the field, but in your code it's a Hash.
I'd suggest adjusting your show action to set #comment = #blog.comments.build, and change the view to read f.hidden_field :blog_id.

saving comments object with relationship to a status update .build ruby

I have a status update, and comment db table.
A user has many status updates, and a status update has many comments. Similar to facebook, When a users friend goes to the users feed page (show page), they should be able to comment on the users status updates.
I'm having issues saving a users friends comment.. my code is below.. I think it has something to do with the Comments Controller, Create method, "#comment = #statusupdate.comments.build(params[:comment])"
any guidance is much appreciated! thanks!
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#statusupdates = #user.statusupdates.paginate(:page => params[:page], :per_page => 25)
#statusupdate = Statusupdate.new
#comment = Comment.new
end
end
show.html.erb
<% form_for #statusupdate do |f| %>
<%= f.error_messages %>
<div class="field">
<%= f.text_field :content %>
</div>
<% #statusupdates.each do |s| %>
<%= s.content %><br />
<% form_for #comment do |f| %>
<%= f.error_messages %>
<div class="field">
<%= f.text_field :comment %>
</div>
<div class="field">
<%= f.hidden_field :user_id, :value => current_user.id %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<br><br>
<% end %>
<% end %>
class CommentsController < ApplicationController
def create
#comment = #statusupdate.comments.build(params[:comment])
if #comment.save
flash[:success] = "Comment created!"
redirect_to root_path
else
#feed_items = []
render 'pages/home'
end
end
end
Check the html of the form to make sure its right. Also see what parameters are getting sent to the create action.
The main thing I see is that the forms for the status update and the comments are nested, and both use the block parameter f. This could cause things to get very strange (especially since the scoping of block parameters differs between ruby 1.8 and 1.9). It also seems like you don't actually want the forms nested. You should also probably fix the indentation in your html.
show.html.erb- I changed the top line of the status update comment form to:
<% form_for (s, s.comments.build) do |f| %>
...
class CommentsController < ApplicationController
def create
#statusupdate = statusupdate.find(params[:statusupdate_id])
#comment = #statusupdate.comments.create(params[:comment])
...
I don't exactly know what's going on here but it worked for me~ hope it helps someone out~

Resources