I am trying to add a comments form to a blog post, and have historical comments displayed, too. I created two partials. I can see the comment form in the posts/show view, and I can leave comments, and the comments are added to the database, but those comments are
not displayed.
How can I get these historical comments to be displayed?
Here are my relevant files/excerpts:
app/views/posts/show.html.erb
<%= render partial: '/comments/comment', locals: { comments: #comments} %>
<%= render partial: '/comments/form', locals: { comment: #comment } %>
app/views/comments/_comment.html.erb
<% comments.each do |comment| %>
<%= comment.body + " " + comment.user.name %>
<% end %>
app/views/comments/_form.html.erb
<%= form_for [#post, comment] do |f| %>
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit "Save" %>
<% end %>
app/controllers/posts_controller.rb
def show
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
#comments = #post.comments
#comment = Comment.new
end
app/controllers/comments_controller.rb
def create
#post = Post.find(params[:post_id])
#topic = #post.topic
#comment = current_user.comments.new(comment_params)
#comment.post = #post
if #comment.save
redirect_to [#topic, #post], notice: "Comment was saved successfully."
else
flash[:error] = "Error creating comment. Please try again."
render :new
end
end
Thanks.
As far as I can see, if you're sure the comments are being created correctly (and associated with the right post), then there are two main possibilities:
There's something wrong with the way you're rendering the partial. The convention is to leave off the leading /, and I think it might lead to the wrong path (so comments/comment). That said, I think that would raise an error if it didn't find the file, not be silent.
There's some sort of styling or conditional logic hiding the partial. Is there a condition around it that might be false? Is anything display: none for any reason?
The #comments variable could be instantiating incorrectly, but it looks reasonable to me. You can test that out by just adding this line to the top of the show view, and seeing what is printed:
<%= #comments %>
If a bunch of comments are printed, then I'd take that line out, and see what happens when you add <%= comments %> just inside the partial. Narrow down the problem, til you see where it is -- because your above code looks good to me when I look it over.
Related
Hi I am quite new to Rails and am just setting up comments for my Shop_Profile model. I am using the acts_as_commentable gem to allow for polymorphic comments. I am allowing comments on the profile Show page so am displaying the list of comments and the new comment form on the same page.
My Show Action in the ShopProfilesController looks like this:
def show
#comments = #shop_profile.comments
#comment = #shop_profile.comments.new
end
And I am rendering the comment form and comments in the show view with:
<% if user_signed_in? %>
<%= render 'comments/form' %>
<% end %>
<%= render #comments %>
My Create action on my comment controller is:
def create
#comment = #user.comments.build(comment_params)
#commentable = Comment.find_commentable(params[:comment][:commentable_type], params[:comment][:commentable_id])
if #comment.save
redirect_to #commentable
end
end
and my _comment partial is:
<p>
<strong>Title:</strong>
<%= comment.title %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.comment %>
</p>
<p>
<small>By:</small>
<%= comment.user.username %>
</p>
The new #comment for the form keeps getting included in the #comments and is thus causing an error "undefined method `username' for nil:NilClass" because the new #commentn has no user_id.
How can I display my #comments without including this new #comment for the form_for?
Thanks for any help
Unfortunately (in this case) new/build adds the built object to the association's collection. So you'll need to declare your intent that you only want items stored in the database for the #comments collection.
You have two options I know of off the top of my head:
def show
#comment = #shop_profile.comments.new
#comments = #shop_profile.comments(true)
end
This forces the #comments to be loaded cleanly, so it will only contain the original list. Unfortunately you're hitting the database twice for the same list, that's silly.
Even better, I think, for this would be to do:
def show
#comments = #shop_profile.comments.to_a
#comment = #shop_profile.comments.new
end
So now you detatch the #comments collection from the active record association by making it an array, so the new call later on won't modify anything you still are holding on to.
You're creating an additional comment in your collection, and that new comment doesn't have an associated user yet, and isn't saved in the database yet.
If you want the new comment to be skipped entirely, you can do this:
<%= render #comments.reject{|c| c == #comment } %>
If you want the new comment to show up, but skip the "By" section, you can do this:
<% if comment != #comment %>
<p>
<small>By:</small>
<%= comment.user.username %>
</p>
<% end %>
I have RoR 4.2.0beta. (Although it s irrelevant as this is a beginer problem).
My form does not insert in the database the "propuneres" that I am creating trough it. And as a result they do not show in the index page when I get redirected to it. They show up when I create them through the console.
class PropuneresController < ApplicationController
before_action :prop_params
def new
#user = User.find(params[:user_id])
#propunere = #user.propuneres.build
end
def create
#user= User.find(params[:user_id])
#propunere = #user.propuneres.new(params[:prop_params])
#propunere.save
if #propunere.empty?
render 'new'
else
redirect_to user_propuneres_path
end
end
def index
#user = User.find(params[:user_id])
#propunere = #user.propuneres(params[:prop_params])
end
private
def prop_params
params.require(:propunere).permit(:titlu, :body)
end
end
new.html.erb
<h2> Propunere Nouă </h2>
<%= form_for #propunere do |f| %>
<ul>
<% #propunere.errors.full_messages.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<p>
<%= f.label :titlu %><br />
<%= f.text_field :titlu %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
index.html.erb
<h2> Propuneri: </h2>
<% #propunere.each do |p| %>
<%= p.titlu %>
<%= p.body %>
<% end %>
Not sure if its relevant, but you have code
#propunere.save
if #propunere.empty?
render 'new'
else
redirect_to user_propuneres_path
end
Object #prorunere will never be empty, since you have
#propunere = #user.propuneres.new, which assigneds user_id to your #propunere object and
render 'new' will never be rendered, therefore you wont see any validation errors and never find out why your record wasnt created
Also since you have that piece of code, and dont see errors, this is what most like broke your code
#user.propuneres.new(params[:prop_params]) - you should use your permitted params, so it'd look like
#propunere = #user.propuneres.new(prop_params)
I've cloned your repo, here's the problem: in new.html.erb you had
<%= form_for #propunere, url: new_user_propunere_path(#user, #propunere), html: { method: :get } do |f| %>
Both the url and the method are wrong. user_propuneres_path will give you the correct url for the create action and the correct method is :post, not :get. This is why you never reached the create action.
You also need to change from #propunere = #user.propuneres.new(params[:propunere]) to #propunere = #user.propuneres.new(prop_params), otherwise you'll get a ActiveModel::ForbiddenAttributesError exception.
You can see all the routes in the app by running rake routes in the terminal.
I don't think you need the params in your index action:
#propunere = #user.propuneres
and it would be more logical to write it in plural since you have many of them.
Edit:
As Avdept suggested your create action should look like this:
def create
#user = User.find(params[:user_id])
#propunere = #user.propuneres.new(prop_params)
respond_to do |format|
if #propunere.save
format.html { redirect_to user_propuneres_path(#user), notice: 'Your propunere has been saved' }
else
format.html { render :new }
end
end
end
Do you have any validations for the Propunere model? Maybe the model is invalid. You can use
the create! method instead of create for testing, because it will throw an exception if the object cannot be saved. Also try puts #propunere.inspect before persisting it and check that the contents of the object are ok, the output will be shown in the development log.
I'm new to programming and have been learning Ruby on Rails for about 6 weeks. I've added commenting functionality to my app, and while comments are being displayed properly, so is everything else in the (sqlite3) database associated with the comment - created_at, updated_at, comment_id, post_id.
The partial that displays comments has the following code:
<%= form_for [post, comment] do |f| %>
<p><%= #comments.each do |comment| %></p>
<small>
<p><%= comment.body %></p>
</small>
<% end %>
<% end %>
As you can see, I'm only trying to display the comment body, but I'm displaying everything.
Here is the create method from the comments controller:
def create
#post = Post.find(params[:post_id])
#comment = current_user.comments.build(params_comment)
#comment.post = #post
authorize #comment
if #comment.save
flash[:notice] = "Comment was created"
redirect_to [#post.topic, #post]
else
flash[:error] = "Comment failed to save"
redirect_to [#post.topic, #post]
end
end
end
I'm not sure why everyting is displaying if I'm only calling .body on comment. I've researched this problem but haven't found anything. Any help would be appreciated.
Here is the fix :-
<%= form_for [post, comment] do |f| %>
<!-- Here I removed `=` from `<%` %> -->
<p><% #comments.each do |comment| %></p>
<small>
<p><%= comment.body %></p>
</small>
<% end %>
<% end %>
#each returns the collection when the block is finished with full iteration. Now, you used <%= ..%>, which then printing the return value of #each. But if <%..%>, it wouldn't print although the #comments.each still returning the #comments collection.
I'm pretty sure that this is a simple fix but I'm not seeing it. I have an app where I'd like to show a music_videos comments. Below are my controllers:
def show
#music_video = MusicVideo.find(params[:id])
#comment = Comment.new
#comments = #music_video.comments.page(params[:page]).per(3)
end
The above is my music video controller.
def create
#music_video = MusicVideo.find(params[:music_video_id])
#comment = #music_video.comments.build(comment_params)
if #comment.save
flash[:notice] = "Comment Submitted"
redirect_to music_video_path(#music_video)
else
render 'music_videos/show'
end
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_to root_path, notice: "Comment Deleted"
end
private
def comment_params
params.require(:comment).permit(:body)
end
Above is my comments controller
Finally my show page:
<div class="comments_row">
<% #music_video.comments.each do |comment| %>
<% if user_signed_in? && current_user.admin? %>
<p class="comment"><%= comment.body %></p>
<%= link_to 'Delete Comment', music_video_comment_path(#music_video,comment),
method: :delete %>
<% else %>
<p class="comment"><%= comment.body %></p>
<% end %>
<%end%>
</div>
<%= paginate #comments %>
I'm pretty sure something is wrong with my controllers, but I'm not sure exactly what it is. #comments is in the correct controller (MusicVideo) within the correct CRUD operation (show). Currently I have six comments in a particular show page and the pagination shows up just fine but the six comments are not paginated. Any thoughts?
EDIT-------------
I figured out one the problem but stumbled on a new one. I figured out that in my controller I am declaring #comments = pagination etc. etc. when in my views there is no #comments to paginate. The problem is now that when I use
<%= paginate #comment %>
the code will break.the problem now that I'm having is what variable to paginate. Trying this code will also break
<%= paginate #music_video.comments %>
Any recommendations?
I set up a test application using the kaminari gem for pagination. This is what my my music video controller's show action looks like:
def show
#music_video = MusicVideo.find(params[:id])
#comments = #music_video.comments.page(params[:page]).per(3)
end
And here is what my show view looks like:
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong> <%= #music_video.name %>
</p>
<% #comments.each do |comment| %>
<p>
Comment: <%= comment.text %>
</p>
<% end %>
<%= paginate #comments %>
<%= link_to 'Edit', edit_music_video_path(#music_video) %> |
<%= link_to 'Back', music_videos_path %>
It is working and the pagination is showing up for me.
I think one thing i see directly is that you should use <% #comments.each do |comment| %> instead of <% #music_video.comments.each do |comment| %> because the way you have it now it will display all comments for the video regardless of what page you are on. If you had 6 comments and wanted 3 per page you would see the pagination with the two pages because you're running your pagination based off of #comments and you would end up seeing all 6 comments on both pages because you're doing your .each with #music_videos.comments.each.
So, at least using #comments in both places would be a start. And make sure you're using <%= paginate #comments %> for the pagination. If you use this in your controller and view what do you get? Do you see any comments?
Also, Ryan Bates has a great screencast on Kaminari as well: http://railscasts.com/episodes/254-pagination-with-kaminari (that site is a great resource for rails questions)
I have a form for a user to create a question (in additon to user model, there's a question model, with nested answers) on their profile page. It submits from the users profile page /views/users/show.html.erb to the create action of the questions_controller.rb. If it doesn't validate, I think the default for Rails is to render the form(with the invalid information in the form for the user to edit). However, since I'm submitting the form for the question model from the users profile page the prepopulation isn't happening upon failed validation; the user is forced to enter all the information in the form again. Is there a way in this context to get the form on the users show page to fill out with the information that was entered prior to submission?
questions_controller
def create
#question = current_user.questions.build(params[:kestion])
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
###render :action => 'show'
redirect_to current_user
end
end
Update
I've changed the end of the create method too
Redirect ( : back ), :notice => "something went wrong.try again"
But I still can't get the form to populate, and the validation error messages aren't showing either, only the flash notice.
Update
The show method of the users controller creates the new Question (along with the user)
def show
#user = User.find(params[:id])
#question = Question.new
3.times {#question.answers.build}
end
The /views/users/show.html.erb
<%= form_for #question do |f| %>
<% if #question.errors.any? %>
<h2><%= pluralize(#question.errors.count, "error") %> prohibited this question
from being saved: </h2>
<ul>
<% #question.errors.full_messages.each do |msg| %>
<li> <%= msg %></li>
<% end %>
</ul>
<% end %>
<p>
<%= f.label :content, "Question"%>
<%= f.text_area :content, :class => 'span4', :rows => 1 %>
</p>
<p>
<%= f.label :link, "QuoraLink" %>
<%= f.text_field :link, :class => 'span4', :rows => 1 %>
</p>
<%= f.fields_for :answers do |builder| %>
<p>
<%= render 'answer_fields', :f => builder %>
</p>
<% end %>
<p><%= link_to_add_fields "Add Answer", f, :answers %></p>
<p><%= f.submit %></p>
<% end %>
the answer_fields partial rendered from the questions partial
<p class="fields">
<%= f.label :content, "Answer..." %>
<%= f.text_field :content, :class => 'span3', :rows => 1%>
<%= f.label :correctanswer, "Correct" %>
<%= f.check_box :correctanswer, :class => 'span1' %>
<%= link_to_remove_fields "remove answer", f %>
</p>
Currently, in views/users/show.rb you do
#question = Question.new
that creates an empty new question. Then you populate the forms with this empty model.
What you could do instead is:
if session[:question]
#question = #user.questions.new(session[:question])
session[:question] = nil
#question.valid? # run validations to to populate the errors[]
else
#question = Question.new
end
Now all what's left to do is populating session[:question] in your questions_controller before redirecting to :controller=>"users", :action=>"show". Something like:
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
session[:question] = params[:question]
redirect_to current_user
end
You may need to work on serialization/deserialization additionally for populating/using session[:question]. I didn't try to compile, so am not sure.
All this is needed because when you do redirect_to your processing of the user request ends, the user browser gets a redirect status code from your server and goes for another page, sending you a new request (which lands on the path, and eventually controller/action, to which you redirected to). So, as soon as you return from the request processing, all your variables are lost. For the next request you start from scratch.
The render :action => "show" approach (that was in the original scaffold and that you commented out) worked because you didn't return back to user but simply rendered the template with a specific name using the variables you already had in place (including #question, on which 'save' was called and failed, and thus internally validations were called and populated the errors object).
Actually, that reminded me that you may want to use another approach. Instead of passing parameters through session[] and redirecting to UsersController, you may want to populate all required variables and just render the view from that controller. Like below:
if #question.save
redirect_to current_user, :notice => "Successfully created question."
else
#user = current_user
render "users/show"
end
Firstly, the reason that using redirect_to instead of render doesn't repopulate the form is that when you redirect_to, the controller logic for the action is run, whereas using render ignored the controller logic.
So when you render :action => 'show' (the "default" behaviour), it renders show.html.erb, with #question set like this:
#question = current_user.questions.build(params[:kestion])
When you redirect_to current_user, it renders show.html.erb with #question set using the code in your show action:
#question = Question.new
3.times {#question.answers.build}
This is why you get a new (blank) form, instead of a pre-populated one.
Is it really that important that you use redirect_to? If it is, you'll need to get your show method to do the validation. For example, you could rewrite your show method to something like:
def show
#user = User.find(params[:id])
if params.has_key?(:kestion)
#question = #user.questions.build(params[:kestion])
else
#question = Question.new
3.times {#question.answers.build}
end
end
and then make your form point at that page, with something like:
<%= form_for(#question, url: users_path(#question.user) do |f| %>
...
<% end %>
(depending on how your routes are set up and named). Of course, by that point the whole thing become horribly un-RESTful, a bit of a mess, and definitely not the Rails way of doing things. (The other, worse option would be to redirect back and pass the params through a get query.) In my opinion, you lose a lot for a minor gain, and I'm not sure that I'd really recommend it.