I am adding comments to a post which save fine but I have been struggling to get them show more than one comments on a post. When I save it only shows the first comment posted.
Comments Controller:
class CommentsController < ApplicationController
def create
#micropost = Micropost.find_by(id: params[:micropost_id])
#comment =
#micropost.comments.create(params[:comment].permit(:body))
if #comment.save
flash[:success] = "Comment Posted"
end
redirect_to current_user
end
end
def show
#comment = Comment.find_by(id: params[:id])
end
end
In the view I have:
<%= #comments.body %>
I can show one comment, but when I write another the first one only shows. I tried to loop over them in the view with a do loop but I then get an error stating undefined method .each for "text from comment":String
Maybe my show #comment needs improving or is there something I have to do in the view to get them all to display. The same do loops works for the posts, but I can seem to get it working for the comments.
I also have another minor issue when there is no comments saved, I can't view the post at all because there is no body existing. I assume I will have to write some sort of if/else to state if there is no body, display anyway.
In your posts controller's show method you should fetch comments and then you can show it in html page.
For example,
def show
#post = Post.includes(:comments).find(params[:id])
end
and in your view file, you can iterate over comments like below,
#post.comments.each do |comment|
# you logic here
end
what you are doing is fetching single comment and try to loop through that object , which is not possible.
what you want to do is display all the comments posted on 'Micropost' so what you need to do is just pass micropost_id in params and user where query like below
#comments = Comment.where(micropost_id: params[:micropost_id])
then you can loop through that object in your .erb template
<% #comments.each do|comment| %>
<%= comment.body %>
<% end %>
Related
Can't seem to figure this one out... In my rails app, I have Post and Comment resources. When a new comment is created, I want to reload the list of comments using AJAX. That part seems to work appropriately—however, when the full page is subsequently reloaded, the list also shows a duplicate of the comment, as shown in this screenshot. Any thoughts on what may be causing this?
(note: deleting one of the comments also deletes the duplicate)
views/users/show.html.haml
= form_for([post, post.comments.build], remote: true) do |f|
= f.text_field :content, placeholder: 'Press ENTER to submit...', class: "comment_content", id: "comment_content_#{post.id}"
- if post.comments
.comments{ id: "comments_#{post.id}" }
- post.comments.each do |comment|
= render post.comments, post: post
views/comments/_comment.html.haml
- unless comment.content == nil
.comment{ id: "comment_#{comment.id}" }
.user-name
= link_to comment.user.identities.first.nickname, comment.user
.comment-content
= comment.content
- if comment.user == current_user
= link_to post_comment_path(post, comment), data: { confirm: "Are you sure?" }, method: :delete, remote: true do
%i.fa.fa-close
controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :set_post
def create
#comment = #post.comments.build(comment_params)
#comment.user_id = current_user.id
if #comment.save
respond_to do |format|
format.html { redirect_to :back }
format.js
end
else
render root_path
end
end
...
views/comments/create.js.erb
$('#comments_<%= #post.id %>').append("<%=j render #post.comments, post: #post, comment: #comment %>");
$('#comment_content_<%= #post.id %>').val('')
Update
Following #uzaif's suggestion, I replaced ".append" with ".html". This fixed the problem only if I also moved the code out of the _comment partial. Not really an ideal solution... I'd still like to know if/how I could fix the problem and keep my individual comment partial.
The Problem
You are seeing duplicate comments because you are inadvertently rendering your comments twice.
- post.comments.each do |comment|
= render post.comments, post: post
Calling render and passing a collection as the first argument instructs Rails to render all of the comments. Internally Rails will iterate over each comment and render it using the object's partial.
By wrapping this render call (which already has a loop in it) within another loop, you are actually rendering each comment twice.
The Solution
Remove the outer post.comments.each loop and just let the render method do it's thing.
= render post.comments
Rails knows to pass along an local variable named comment to each partial, and you should be able to reference the original post by calling comment.post (assuming comment belongs_to :post).
Be careful how you call render
There are a couple different ways to render partials with data from a collection. Make sure you know which one you are using and don't mix-and-match them.
I describe this in another StackOverflow post.
Resources
http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-collections
https://stackoverflow.com/a/40412677/1219460
I am making a portfolio page in rails. On the front page I have an "About" section where I have a personal description of myself. I want to be able to change this dynamically (not hard-coded html).
I would like to have the description be a text variable or string that I can modify through a form in the view section.
Questions
1. How should I declare this variable in the controller?
2. How do I access and change it from the form in the view?
3. Is there a better solution to my problem?
The only way to do this is to send the updated values to your controller. You need to have a form on your portfolio page:
#config/routes.rb
resources :users do
resources :portfolios #-> url.com/users/:user_id/portfolios/:id
end
#app/controllers/portfolios_controller.rb
class PortfoliosController < ApplicationController
def show
#user = User.find params[:user_id]
end
end
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:about)
end
end
#app/views/portfolios/show.html.erb
<%= form_for #user do |f| %>
<%= f.text_field :about %>
<%= f.submit %>
<% end %>
Without any more context, that's the best I can give.
You will need to connect a database to store and retrieve dynamic stuff. You can access a variable in views if you define it with # like;
#about= Me.last.about
where Me could be your model that the information is saved in and Me.last would be the instance of that model. You can update this information by updating the model like
Me.last.update_attributes :about=> params[:about]
where params[:about] would be the params from the field in the form.
I would recommend following a guide so you get a complete solution. Below I have the major steps a user would take to update some content. This code is not a complete solution.
Your form would submit the updated content to another controller action (through another route).
<%= form_for #user do |f| %>
In that controller, you would store the content (usually in the database).
def update
#user = User.find(params[:id])
#user.update(user_params)
redirect_to :back
end
The original controller action would have a variable that got the content from the place you stored it
def show
#user = User.find(params[:id])
end
I have a Book resource and a Comment resource that belongs to Book.
On app/views/book/show.html.erb, I show the book information and all the comments for the book. A user can add comments directly from that page.
So the BooksController#show action looks like this:
def show
#book = Book.find(params[:id])
#comment = Comment.new
end
And the app/views/book/show.html.erb looks like this:
# ... book info from #book
<%= form_for [#book, #comment] do |f| %>
# ... rest of the form_for
<% #book.comments.each do |comment| %>
# ... print all the comments
So this goes to CommentsController#create action:
def create
#book = Book.find(params[:book_id]
#comment = #book.comments.new(comment_params)
if #comment.save
redirect_to #book
else
render 'book/show'
end
end
Now all this is good, but when this action fails and renders book/show, I can see the new comment in the comments list, because it's added to that book's comments collection, even though it's not saved to the database yet.
What's the rails way to handle this?
You can add a scope to only render persisted comments
app/models/comment.rb
class Comment < ActiveRecord::Base
scope :persisted, -> { where "id IS NOT NULL" }
end
app/views/book/show.html.erb
<% #book.comments.persisted.each do |comment| %>
You could try redirecting to book/show instead of rendering it directly.
Instead of your conditional, just always redirect. You may want to inform the user of errors though, so you can set a flash message.
if #comment.save
redirect_to #book
else
redirect_to #book, :flash => { :error => "Something went wrong!" }
end
You could build your comment differently:
def create
#book = Book.find(params[:book_id]
#comment = #comments.new(comment_params.merge { book_id: #book.id })
if #comment.save
redirect_to #book
else
render 'book/show'
end
end
I first tried using the following in the app/views/books/show.html.erb:
<% #book.reload.comments.each do |comment| %>
# ... print all the comments
This reloads the book object from the database so the new comment that is not saved to the database yet is not shown. But the downside is that this hits the database one more time, which is unnecessary.
So I ended up using this:
<% #book.comments.select(&:persisted?).each do |comment| %>
# ... print all the comments
This only shows the persisted comments.
I'll probably take this logic off the view template, and put it in a view helper.
I am fairly new to Ruby on Rails. I have set up a basic User / Post Model which allows a User to create Posts. I am trying to create Comments for the Posts, which will also relate a Comment to the User who created it.
I have set up the relationships so that a User has many Posts and Comments, a Post belongs to a User and has many Comments, and a Comment belongs to both a Post and a User.
My problem comes in when trying to save a Comment. In my Comments controller:
def create
#comment = current_user.comments.build(params[:comment])
end
This does not work and says nothing about which Post the Comment belongs to. I am also displaying the 'Add Comment' form on the Show Posts page in order to create a Comment Wall below the Post, but my form still just says
<%= form_for #comment do |f| %>
And does not relate the comment to the Post in the Posts controller under Show.
I realize there is probably a straightforward answer but could not find any resources that explained my specific issue. Any help is appreciated.
I would say that you're primarily adding the comment to a specific post. So start there:
def create
#post = Post.find(params[:post_id]) # <== this may be just `id`
#comment = #post.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
# ... do things
else
# .. render error
end
end
For this to work you're going to need your route to be nested:
resources :posts do
resources :comments
end
And your form_for will need to indicate the nested route:
<%= form_for [#post, #comment] %>
You are doing the right thing in CommentsController#create
#comment = current_user.comments.build(params[:comment])
The part that you're missing is include the post_id in your params[:comment]. That should be no problem because you always know the post you're commenting on, the one that you're showing. So add the following inside your form_for #comment
<%= f.hidden_field :post_id, #post.id %>
I am assuming this is in your show post view, and you have set #post in your PostController#show
You can do:
def create
#comment = Comment.new(params[:comment])
#comment.user = current_user
if #comment.save ...
I want to show a post along with the poster's info on my rails app. Right now I'm able to show the post and user association on the "show" page (the page for a single post), but when I want to show it on the "index" page (where all of my posts are), I get this error: undefined method `username' for nil:NilClass
I added #post.user = current_user to my post_controller under "show" (That allowed me to show the poster's info. But i dont know what to add to the "def index".
This is what I have there:
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
end
end
This is what I have under "show"
def show
#post = Post.find(params[:id])
#post.user = current_user
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #post }
end
end
I want to know what to add to my def index to be able to display the poster's info. Thanks in advance.
Your set up is wrong. The user needs to be added to the post when it is created and updated.
As you have it now, all posts will always belong to the user that is currently viewing the post because you are assigning the current_user to the post in the show action (and would be doing the same in the index view if you continued with this approach) meaning that when post 1 is viewed by user 1 it would show as belonging to user 1. When post 1 is shown by user 2 it would belong to user 2.
The solution is to assign the current user to the post in the update and create actions of the posts controller exactly as you have it now in the show action but before the post gets physically updated or created.
Then remove the #post.user = current_user from the show action.
This may mean you are left with legacy data in your app that doesn't have a user assigned. But that's o.k., just edit and save each post and it will have you attached automatically.
Then in the views/posts/index_html.erb just add the user details that you want to see in new table row columns before the show/edit links. This is assuming you have a standard scaffolded index.html.erb file. If you don't have a standard index view then put it wherever you want it but then if you had a customised index view you probably wouldn't be asking this question in the first place so forget about that and you'd know where it goes
Update
I did explain how to show the user in the views in my response above but possibly I need to be a little clearer so I'll show you the code.
To show the user name in the show view use
<%= #post.user.name unless #product.user.blank? %>
To show the user in the index action for the post use
<% #posts.each do |post| %> <!-- This is already in your index action. -->
<%=post.user.name unless post.user.blank?%><!-- This is the code you need -->
<!-- You might want to add a new th in the header for the css table then you can add a <td></td> tags round the above line so your table all matches up nicely for display purposes-->
<%end%> <!-- This is already in your index action -->
Hope that helps