form_for on commentables in rails - ruby-on-rails

I'm having problems with the "First argument in form cannot contain nil or be empty" error that I haven't been able to find an answer for.
Basically, I have Posts, which show up on a User's show page, which should each have the option to comment on them. My routes are as follows:
resources :posts, only: [:create, :destroy] do
resources :comments, only: [:create, :destroy]
end
users/show.html.erb
<ol class="posts">
<%= render #posts %>
</ol>
posts/_post.html.erb
<li>
<span class="content"><%= post.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(post.created_at) %> ago.
</span>
<% if current_user?(post.user) %>
<%= link_to "delete", post, method: :delete,
data: { confirm: "You sure?" },
title: post.content %>
<% end %>
<span class="content">
<ul> Comments: <%= post.comments.count %></ul>
<% post.comments.each do |comment| %>
<ul> <%= comment.comment %> </ul>
<% end %>
</span>
<% if post != nil %>
<% form_for [post, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Comment..." %>
</div>
<%= f.submit "Post", class: "btn btn-lg btn-primary" %>
<% end %>
<% end %>
</li>
s comments_controller.rb
def create
#post = Post.find(params[:id])
#comment = #post.comments.build(comment_params)
#comment.user = current_user
if #comment.save
flash[:success] = "Posted!"
redirect_to #post
else
render 'static_pages/home'
end
end
def destroy
#comment.destroy
redirect_to root_url
end
Showing C:/app/views/posts/_post.html.erb where line #21 raised: (line 21 is the form_for line)
First argument in form cannot contain nil or be empty
app/views/posts/_post.html.erb:21:in _app_views_posts__post_html_erb___306000501_37434348'
app/views/users/show.html.erb:19:in_app_views_users_show_html_erb__480533737_37130988'
If each post had a show page, I know that I would put #posts on the show action and then make it an instance variable, but since the each post needs a separate comment dialogue box, I can't make it that general. It doesn't go through the posts controller though so I can't make it specific to each post. I'm using acts_as_commentable for the comments. Ideally, I would like to make them generic and put all the form_for comment stuff into a partial, but I'll figure that out later.

Related

Defining users within partials

I am new to ruby on rails, and know this is a beginner error. I'm trying to create an application that displays user comments within a post. I have a comment partial pointing to my post#show, and when requesting to display <%= image_tag(comment.user.avatar.small.url, class: "media-object") if comment.user.avatar? %> from _comment.html.erb, I get "undefined method 'user' for nil:NilClass. I've read other questions on stackoverflow and I know this is saying it can't find a user associated with the post within comments and that I need to define user in my controller. I'm assuming it's for the post controller, but I can't seem to wrap my head around the specific syntax to display comments by user in post.
#comments_controller
def create
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:post_id])
#comment = current_user.comments.new(comment_params)
#comment.post = #post
#comment.save
authorize #comment
redirect_to [#topic, #post]
end
#posts_controller
def show
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
end
#post model
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_one :summary
has_many :comments
mount_uploader :image, ImageUploader
#comment model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
#rendering comment partial in post#show
<h1><%= #post.markdown_title %></h1>
<div class="row">
<div class="col-md-8">
<small>
<%= image_tag(#post.image.thumb.url) if #post.image? %></br>
<%= image_tag(#post.user.avatar.tiny.url) if #post.user.avatar? %>
submitted <%= time_ago_in_words(#post.created_at) %> ago by
<%= #post.user.name %>
</small>
<p><%= #post.markdown_body %></p>
</div>
<div class="col-md-4">
<% if policy(#post).edit? %>
<%= link_to "Edit", edit_topic_post_path(#topic, #post), class: 'btn btn-success' %>
<% end %>
<% if policy(#post).destroy? %>
<%= link_to "Delete Post", [#topic, #post], method: :delete, class: 'btn btn-danger', data: { confirm: "Are you sure you want to delete this post?" } %>
<% end %>
</div>
<div class="col-md-8">
<% if policy(Comment.new).create? %>
<%= render partial: 'comments/comment', locals: { comment: #comment } %>
<%= render partial: 'comments/form', locals: {topic: #topic, post: #post} %>
<% else %>
<h3>Sign up to Bloccit to post comments</h3>
<%= link_to "Sign up", new_user_registration_path, class: 'btn btn-primary' %></br></br>
<% end %>
#comment partial
<ul>
<% #post.comments.each do |c| %>
<div class="media">
<div class="media-left">
<%= image_tag(comment.user.avatar.small.url, class: "media-object") if comment.user.avatar? %>
</div>
<div class="media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if policy(comment).destroy? %>
| <%= link_to "Delete", [#topic, #post, comment], method: :delete %>
<% end %>
</small>
<li>
<p><%= c.body %></p>
</li>
</div>
<% end %>
Also, when I put #post.user = #user in posts_controller the error changes to "undefined method 'avatar' for nil:NilClass. Thanks for your help!
undefined method 'user' for nil:NilClass
The error is because of this line
<%= render partial: 'comments/comment', locals: { comment: #comment } %>
You don't have #comment initialized in show method of posts_controller and you are passing it as locals for comment, so comment.user returns error.
And also I've noticed that in _comment.html.erb you have this line <% #post.comments.each do |c| %>, so you can use c instead of comment.
<%= image_tag(c.user.avatar.small.url, class: "media-object") if c.user.avatar? %>
You are incorrectly referencing the block variable. Either change |c| to |comment| or change the comment references to c.
So like this:
<%= image_tag(c.user.avatar.small.url, class: "media-object") if c.user.avatar? %>

Couldn't find Post without an ID / ActiveRecord::RecordNotFound

I know this is a very basic solution however, I am just not seeing it right now. I am getting a 'Couldn't find Post without an ID' error within CommentsController#create.
I created a 'New Comment' button under the post which should then redirect to the comment form. From there once a user inputs their comments and clicks the 'Create Comment' button the comment should be displayed under the original post. Thank you in advance.
Comments Controller
Class CommentsController < ApplicationController
before_filter :authenticate_user!
def new
#comment = Comment.new
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:commenter, :body))
respond_to do |format|
if #comment.save
format.html { redirect_to #post, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
def show
#comment = Comment.new
end
end
Post/Show
<div class="row">
<div class="col-md-offset-4 col-med-8">
<div class="panel panel-default">
<div class="panel-heading center">
<% if #post.image.url %>
<%= image_tag #post.image.url(:medium) %>
<% elsif #post.video.url %>
<%= video_tag #post.video.url(:medium), controls: true, type: "video/mp4" %>
<% end %></br>
<p>
<strong>Likes:</strong>
<%= #post.get_likes.size%>
</p>
<%=link_to image_tag('like.jpg', :border => 0), likes_post_path(#post) %>
<%=link_to image_tag('unlike.jpg', :border => 0), dislikes_post_path(#post) %>
</div>
<div class="panel-body">
<p><%= #post.description %></p>
<% user = #post.user %>
<p><strong><%= link_to(user.name, user_path(user)) if #post.user %></strong></p>
<%= link_to 'New Comment', new_comment_path, class: "btn btn-danger btn-sm active" %></br>
<br><% if #post.user == current_user %>
<%= link_to edit_post_path(#post) do %>
<span class="glyphicon glyphicon-edit"></span>
Edit
<% end %>
<% end %>
<%= link_to 'Back', posts_path %>
</div>
</div>
</div>
Comments / _form
<%= form_for [#post, Comment.new] do |f| %>
<% if #comment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#comment.errors.count, "error") %> prohibited this comment from being saved:</h2>
<ul>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit class: "btn btn-danger btn-sm" %>
</div>
<% end %>
Routes
resources :comments
resources :posts do
resources :comments
end
Comments / Show
<p id="notice"><%= notice %></p>
<p>
<strong>Post:</strong>
<%= #comment.post_id %>
</p>
<p>
<strong>Body:</strong>
<%= #comment.body %>
</p>
<%= link_to 'Edit', edit_comment_path(#comment) %> |
<%= link_to 'Back', comments_path %>
Rake Routes
post_comments GET /posts/:post_id/comments(.:format) comments#index
POST /posts/:post_id/comments(.:format) comments#create
new_post_comment GET /posts/:post_id/comments/new(.:format) comments#new
edit_post_comment GET /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment GET /posts/:post_id/comments/:id(.:format) comments#show
I recommend making the following changes:
Routes:
Remove the first resources :comments. Leave the following:
resources :posts do
resources :comments
end
Posts/Show view:
You should be using new_post_comment_path instead of new_comment_path. Run rake routes to see why.
CommentsController#new:
Define #post in your new action:
def new
#post = Post.find(params[:post_id])
#comment = Comment.new
end
Finally, in Comments/_form:
Change <%= form_for [#post, Comment.new] do |f| %> to <%= form_for [#post, #comment] do |f| %>. Although I believe <%= form_for #comment do |f| %> should work.
I recommend going through the Rails Guides for additional information and explanations.
I believe that in def new of the comments controller, you also need to set #post, not just #comment

How can I leave a comment and associate with the post in post' index page?

How can I leave a comment and associate with the post in post' index page?
It is my PostsController:
def index
#posts = Post.all
"what should I add here?"
end
# GET /posts/1
# GET /posts/1.json
def show
#comments = #post.comments.all
#comment = #post.comments.build
end
and its my posts show view:
<p id="notice"><%= notice %></p>
<p>
<h3><%= #post.name %></h3>
</p>
<p>
<%= (#post.descriptopm).html_safe %>
</p>
<%= link_to 'Edit', edit_post_path(#post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Back', posts_path, :class => "btn btn-info btn-xs" %>
<h3>Comments</h3>
<% #comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
and its my posts index view:
<h1>Listing posts</h1>
<%= link_to 'Create a New Post', new_post_path, :class => "btn btn-success btn-sm" %>
<% #posts.each do |post| %>
<div class="post thumbnail">
<h3><%= post.name %></h3>
<div><%= (post.descriptopm).html_safe %></div>
<div class="bottom-bottoms">
<%= link_to 'Display', post, :class => "btn btn-info btn-xs" %>
<%= link_to 'Edit', edit_post_path(post), :class => "btn btn-info btn-xs" %>
<%= link_to 'Delete', post, method: :delete, data: { confirm: 'Are you sure?' }, :class => "btn btn-info btn-xs" %>
</div>
<h3>Comments</h3>
<% post.comments.each do |comment| %>
<div>
<strong><%= comment.user_name %></strong>
<br />
<p><%= (comment.body).html_safe %></p>
</div>
<% end %>
<%= render 'comments/form' %>
</div>
<% end %>
the post.rb :
class Post < ActiveRecord::Base
has_many :comments
end
the comment.rb :
class Comment < ActiveRecord::Base
belongs_to :post
end
the show page's comment function looks right
but when I leave a comment in my post's index page
It might not save the comment with the right post's id
how do I fix it?
and onother one:
how can I redirect the page to the index page after I save the comment not to comment's index page?
How can I leave a comment and associate with the post in post' index
page?
There are several things to consider:
Comment Objects
Firstly, you need to appreciate that since Rails is an object orientated framework (by virtue of being built on Ruby), you will need to ensure your new comment corresponds with the relevant Post object
I think this the core of what you're getting confused about:
def index
#posts = Post.all
# Here needs to go your "comment" build methodology. Except, it only works *per* post ;)
end
The trouble you have is that you can't "build" a comment for Posts - you have to build them per post, like this:
#post = Post.find 2
#post.comments.new #-> allows you to create a new comment for that particular post
--
Implementation
The best solution I can give you will be somewhat constricted, but will serve your purposes correctly. Here it is:
#app/controllers/posts_controller.rb
Class PostsController < ApplicationController
def index
#posts = Post.all
#comment = Comment.new
end
end
#app/controllers/comments_controller.rb
Class CommentsController < ApplicationController
def create
#comment = Comment.new(comment_params)
#comment.save
end
private
def comment_params
params.require(:comment).permit(:comment, :params, :post_id)
end
end
This will take into consideration that for each comment on your index, you'll have a form with the post_id attached; and will be a "linear" flow (IE you can only post a single comment at a time):
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= post.title %>
<%= form_for #comment do |f| %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
<% end %>
Yes, this will give each Post a "new comment" form on your index page (hence my denoting its constriction). However, what it will do is give you the ability to add a comment for any of the posts on the page, handling them with the CommentsController
To answer your second second question, you can use
redirect_to post_index_path
As for the first question, you can set the post id of the post by using:
#post.comments.new
Instead of just Comment.new, which won't set the ID of the post.
In this scenario...this is the best example that you can get help
Railscasts show post with comments using polymorphism

partial form not creating new value RoR

I am new to Ruby on Rails. My Question might sound weird, but i am stuck. i have a form Create that is on html page. i used render partial, but this form is not creating any value. here is my controller code.
Controller.rb
before_filter :signed_in_user, only: [:create, :destroy]
def create
#suppliers = current_user.suppliers.build(params[:supplier_name])
if #suppliers.save
flash[:success] = "Supplier Saved!"
redirect_to 'suppliers/home'
else
render 'suppliers/home'
end
end
def destroy
end
def home
if signed_in?
#supplier = current_user.suppliers.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
my home.html.erb
<div class="row">
<aside class="span4">
<section>
<%= render 'suppliers/suppliers_form' %>
</section>
</aside>
<div class="span8">
<h3>Micropost Feed</h3>
<%= render 'suppliers/feed_supplier' %>
</div>
</div>
My _supplier_form.html.erb is
<%= form_for(#supplier) do |f| %>
<div>
<%= f.text_field :supplier_name, placeholder: "Add new Supplier" %>
</div>
<%= f.submit "Add", class: "btn btn-large btn-primary" %>
<% end %>
My _feed_supplier.html.erb is
<% if #feed_items.any? %>
<ol class="microposts">
<%= render partial: 'suppliers/feed_item', collection: #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>
my _feed_item.html.erb is
<li id="<%= feed_item.id %>">
<span class="user">
<%= link_to feed_item.user.name, feed_item.user %>
</span>
<span class="content"><%= feed_item.supplier_name %></span>
</li>
please guide me to right direction, and if anything else is required then do ask. Thanks in advance
Note: all the mentioned files are in same folder i.e. suppliers. except the controller file
form_for #supplier
will create a parameter hash like
{ :supplier => { :supplier_name => '[some value you put in your form]' }}
so the create action should use
#suppliers = current_user.suppliers.build(params[:supplier])
There is no params[:supplier_name].
I think for the partials, you don't need to write suppliers/*, for example:
<%= render 'suppliers/suppliers_form' %>
could just be
<%= render 'suppliers_form' %>
not sure why that would be causing your app to do what it's doing, but worth a try!

_error_messages does not work for forms

I am working on a project that a user can create a post and others can send posts about that topic.My resources file is :
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :topics , only: [ :show, :create, :destroy] do
resources :posts, only: [:create, :new]
My topics_form.html.erb:
<%= form_for(#topic) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :title, placeholder: "yeni başlık girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>
my create action is :
def create
#topic = current_user.topics.build(params[:topic])
if #topic.save
flash[:success] = "Konu oluşturuldu!"
redirect_to root_path
else
render 'static_pages/home'
end
end
My posts_form.html.erb is :
<%= form_for [#topic, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "yorumunuzu girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>
my post_controller create action is :
def create
#topic= Topic.find(params[:topic_id])
#post = #topic.posts.build(params[:post])
#post.user = current_user
#post.topic_id = #topic.id
if #post.save
flash[:success] = "Yorum oluşturuldu!"
redirect_to topic_path(#topic)
else
render 'static_pages/home'
end
end
This is my 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 |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
If I test the empty post and empty topic. I get this error :
undefined method `each' for nil:NilClass
Extracted source (around line #2):
1:
2: <% #topics.each do |topic|%>
3: <li><%=link_to topic.title, topic_path(topic) %></li>
4:
5: <%= will_paginate #topics %>
My static_pages_controller.rb :
def home
if signed_in?
#topic = current_user.topics.build if signed_in?
end
#topics = Topic.paginate :page => params[:page], :per_page => 20
end
and my home.html.erb:
<% if signed_in? %>
<div class="row">
<%= render 'shared/user_info' %>
<%= render 'shared/topic_form' %>
<ol class="topics-signedin">
<%= render 'shared/topics' %>
</ol>
Why does not errors does not show ?
You are missing #topics in your 'create' action. Yes, it gets set in your 'home', which rendered the form, but on submission, it passed to 'create', which then has to load the variables again, and in your case, there is no #topics set when it goes to render 'static_pages/home'. You need..
...
if #post.save
flash[:success] = "Yorum oluşturuldu!"
redirect_to topic_path(#topic)
else
#topics = Topic.paginate :page => params[:page], :per_page => 20
render 'static_pages/home'
end
You never set #topics, only #topic. So #topics is nil.
And on error you render the static_pages/home, which requires those. You should re-render the new or edit instead.

Resources