Trying to to add a nested post with the ancestry gem - ruby-on-rails

I have topics and posts and they both have relationships. I've been following the guide on how to add a nested post but when i try to reply to a post i get Couldn't find Topic with 'id'=.
In my Posts controller
def create
#topic = Topic.find params[:topic_id]
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.topic_id = #topic.id
if #post.save
redirect_to #topic
else
render :new
end
end
In my Topics controller
def show
#topic = Topic.find params[:id]
#post = Post.new(:parent_id => params[:parent_id])
#posts = #topic.posts
#topic.punch(request)
end
I'll keep it short for how i have my reply button in my topics/show.html.erb page
<% #posts.each do |post| %>
<%= link_to "Reply", new_topic_post_path(#topic, :parent_id => post) %>
<% end %>
Now this is my form
<%= simple_form_for [#topic, #post] do |f| %>
<%= f.hidden_field :parent_id %>
<%= f.input :content, as: :pagedown, input_html: { preview: true, rows: 10 }, label: 'Markdown' %>
<%= f.submit "Post", class: 'button expanded' %>
<% end %>

#Kristján Answered my question. I was missing params[:topic_id]

Related

Rails: Can't render error messages

I'm trying to render errors messages that arise from model validation but I end up with this error:
First argument in form cannot contain nil or be empty
Hopefully the provided code with be of assistance... I was able to at least get the pages to load when I used :comment instead of #comment, but then I didn't know how to then render the errors partial because it was a symbol and not an actual object. Now that I have changed it to an instance variable of an object, I get the error.
Error messages partial
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Comment form
<%= form_for #comment, url: comments_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :post_id, value: post.id %>
<%= f.text_area :content, size: "60x2", placeholder: "Comment on this post..." %>
<%= f.submit "Comment" %>
Post form
<%= form_for [#user, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, size: "60x12", placeholder: "What do you want to say?" %>
<%= f.submit "Post" %>
<% end %>
class CommentsController < ApplicationController
def index
#comments = Comment.all
end
def new
#comment = Comment.new
#user = User.find(params[:user_id])
end
def create
#user = current_user
##post = Post.find(params[:post_id])
#comment = #user.comments.build(comment_params)
if #comment.save
flash[:success] = "Comment Posted!"
redirect_back(fallback_location: root_path)
else
flash[:notice] = "Could not post comment"
redirect_back(fallback_location: root_path)
end
end
private
def comment_params
params.require(:comment).permit(:content, :user_id, :post_id)
end
end
class PostsController < ApplicationController
def index
#posts = Post.all
#user = User.find(params[:user_id])
end
def new
#post = Post.new
#user = User.find(params[:user_id])
end
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Posted!"
redirect_to user_path(current_user)
else
flash[:notice] = "Post could not be submitted"
redirect_to users_path
end
end
private
def post_params
params.require(:post).permit(:content)
end
end
I was able to get it by declaring an #comment variable in my posts#index action because that is where the view is being rendered from

Mistake with my routes for uploading an image

I'm trying to solve a really simple problem with my code. I want to upload image into a post, I use paperclip, and the last step is not working.
That is my controller :
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
flash[:success] = "uccess!"
redirect_to post_path(#post)
else
flash[:error] = #post.errors.full_messages
redirect_to new_post_path
end
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:title, :image, :prix, :adress, :description)
end
end
that my form :
<%= simple_form_for #post, url: root_path do |f|%>
<%= f.input :title, label: "Nom du plat" %>
<br>
<%= f.input :image, as: :file %>
<br>
<%= f.input :prix %>
<%= f.input :adress, label: "Localisation" %>
<%= f.input :description %>
<br>
<%= f.button :submit %>
<% end %>
and that my view :
<%= image_tag (#post.image.url(:medium)) %>
<br>
<%= #post.description %>
<br>
<button>
<%= link_to "Home", root_path %>
</button>
So if you go through, and you spot a stupid mistake, please let me know.
You're calling root_path as the helper in your form when you need to call the route for post. Run rake routes and look for the create action related to post, which should be the same as the get route for the show action.
This should not be root_path:
<%= simple_form_for #post, url: root_path do |f|%>

Adding Comments to multiple models

Im trying to add comments to my topics model the same way you can add comments to posts on my app. i currently have to partials for comments _comment.html.erb and _form.html.erb
_comment :
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<div class= "media">
<div class= "media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if user_is_authorized_for_comment?(comment) %>
| <%= link_to "Delete", [comment.post, comment], method: :delete %>
<% end %>
</small>
<p> <%= comment.body %></p>
</div>
</div>
<% end %>
_form :
<h4>Add a comment</h4>
<%= form_for [post, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
my topic show is :
#DISPLAY Topic comments here
<h3> Comments</h3>
<%= render #topic.comments %>
</div>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, post: #post %>
<% end %>
#------
comment controller :
def create
#post = Post.find(params[:post_id])
comment = #post.comments.new(comment_params)
comment.user = current_user
if comment.save
flash[:notice] = "Comment saved successfully."
redirect_to [#post.topic, #post]
else
flash[:alert] = "Comment failed to save."
redirect_to [#post.topic, #post]
end
end
def destroy
#post = Post.find(params[:post_id])
comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = "Comment was deleted"
redirect_to [#post.topic, #post]
end
end
i have updated the routes for topic comments :
resources :topics do
resources :posts, except: [:index]
resources :comments, only: [:create, :destroy]
end
my question is do i need to create a separate partial to add comments to topics or can i update my _comment partial to work for both post and topic comments . and how can i accomplish this ?
Models
You'll need a polymorphic association on the Comment model:
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
#app/models/topic.rb
class Topic < ActiveRecord::Base
has_many :comments, as: :commentable
end
#app/models/post.rb
class Post < ActiveRecord::Base
has_many :comments, as: :commentable
end
Controllers
This will allow you to save the comments for your various models, the controllers / flow coming secondary:
#config/routes.rb
resources :topics, :posts do
resources :comments, only: [:create, :destroy] #-> url.com/topics/:topic_id/comments
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def create
id = params[:post_id] || params[:topic_id]
if params[:post_id]
#parent = Post.find id
elsif params[:topic_id]
#parent = Topic.find id
end
#comment = #parent.comments.find params[:id]
#comment.save
end
def destroy
#parent = params[:post_id] || params[:topic_id]
#comment = #parent.comments.new comment_params
#comment.destroy
end
private
def comment_params
params.require(:comment).permit(:x, :y)
end
end
Because you're passing the data to the comments controller, you'll only need to evaluate which #parent you're working with.
Views
For your views, you need to pass locals to your _form partial:
#app/views/posts/show.html.erb
<%= render "comments/form", locals: {parent: #post} %>
#app/views/comments/_form.html.erb
<%= form_for [parent, parent.comments.new] do |f| %>
I had this road-block as well and here is what I came up with that passed. I must first give #Richard Peck applause for getting my wheels turning, so thank you :).
Models
Did not implement a polymorphic association. Stuck with has_many and belongs_to, nothing more
Partials
_comment.html.erb
Set up the delete partial to accept "parent" as a local
<div class="media">
<div class="media-body">
<small>
<%= comment.user.name %>
commented
<%= time_ago_in_words(comment.created_at) %>
ago
<% if user_is_authorized_for_comment_via_post?(comment) %>
|
<%= link_to "Delete", [parent, comment], method: :delete %>
<% end %>
</small>
<p>
<%= comment.body %>
</p>
</div>
</div>
_form.html.erb
same idea as _comment.html.erb, see above
<h4>Add a comment</h4>
<%= form_for [parent, comment] do |f| %>
<div class="form-group">
<%= f.label :body, class: 'sr-only' %>
<%= f.text_field :body, class: 'form-control', placeholder: "Enter a new comment" %>
</div>
<%= f.submit "Submit Comment", class: 'btn btn-default pull-right' %>
<% end %>
Setting up CommentController
...
def create
# upon clicking on create, determine what param id is passed
if params[:post_id]
# if it is a post id, set instance of post id as #parent
#parent = Post.find(params[:post_id])
elsif params[:topic_id]
# if it is a topic id, set instance of topic id as #parent
#parent = Topic.find(params[:topic_id])
end
# create instance as #comment. Build/create
# comment belonging to #parent (Topic or Post)
#comment = #parent.comments.build(comment_params)
# The comment must be associated to the current user.
# A comment must have a user, and value of user within instance of #comment
# is currently nil. Set user id as current user
#comment.user = current_user
# save comment to database
if #comment.save
# direction of save through if and elsif
# Redirection depends on the comment's parent.
# .is_a? method determines if it is of a certain class. Here, is #parent
# of class Post? Is #parents is the same parent id passed through params?
if #parent.is_a?(Post) # template error with this included: (== params[:post_id])
flash[:notice] = 'Comment saved successfully'
redirect_to [#parent.topic, #parent]
# if not part of the class Post, is it a Topic? If so, save here and
# redirect to the topic after save
elsif #parent.is_a?(Topic)
flash[:notice] = 'Comment saved successfully'
redirect_to #parent
end
end
end
def destroy
comment = Comment.find(params[:id])
# #topic = Topic.find(params[:topic_id])
# topic_comment = #topic.comments.find(params[:id])
# #post = Post.find(params[:post_id])
# post_comment = #post.comments.find(params[:id])
if comment.destroy
flash[:notice] = 'Comment was deleted'
redirect_to :back
else
flash[:alert] = "Comment counld't be deleted. Try again"
redirect_to :back
end
end
...
Passing in Comments from topic/show and post/show
topic/show
Note: notice how locals are passed into the controller from here
...
<div class="row">
<%= render 'comments/form', comment: Comment.new, parent: #topic %>
</div>
<% #topic.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #topic, comment: comment } %>
<% end %>
...
post/show
<% if current_user %>
<% #post.comments.each do |comment| %>
<%= render partial: 'comments/comment', locals: { parent: #post, comment: comment } %>
<% end %>
<% end %>
<% if current_user %>
<%= render 'comments/form', comment: Comment.new, parent: #post %>
<% end %>
Hope this helps.

Show user's email when commenting rather than asking for input

I created a blog in which users can create comments under posts. Registered users, Admins and Guests will be able to post comments.
So in the _form.html.erb I wrote this:
<%= simple_form_for([#post, #post.comments.build], html: {class: 'form-horizontal' }) do |f| %>
<% if current_user || current_admin %>
<% #comment.commenter = current_user.try(:email) || current_admin.try(:email) %>
<% else %>
<%= f.input :commenter %>
<% end %>
<%= f.input :body %>
<%= f.button :submit %>
<% end %>
However I get this error: undefined local variable or method `comment'.
When I try to change #comment.commenter to #post.comments I get the error: undefined method `each' for "example#person.com":String.
Is there a way set the commenter's name if its registered. Like in the controller maybe?
The controller code is:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def comments_params
params.require(:comment).permit(:commenter, :body)
end
end
If you need any other information please let me know.
Thanks
<%= simple_form_for(#comment),url: [#post,#comment], html: {class: 'form-horizontal' }) do |f| %>
<%= f.input :commenter, value: comment_by_user %>
<%= f.input :body %>
<%= f.button :submit %>
Helper
def comment_by_user
current_user.try(:email) || current_admin.try(:email) if current_user || current_admin
end
Controller
class CommentsController < ApplicationController
before_filter :find_post
def new
#comment = #post.comments.build
end
def create
#comment = #post.comments.create(comments_params)
redirect_to post_path(#post)
end
def destroy
#comment = #post.comments.find(params[:id])
#comment.destroy
redirect_to post_path(#post)
end
private
def find_post
#post = Post.find(params[:post_id])
end
end
Found a solution:
Controller:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(comments_params)
if current_admin || current_user
#comment.commenter = current_user.try(:email) || current_admin.try(:email)
#comment.save
end
redirect_to post_path(#post)
end
View:
<%= simple_form_for([#post, #post.comments.build], html: {class: 'form-horizontal' }) do |f| %>
<% if current_admin || current_user %>
<%= f.input :body %>
<% else %>
<%= f.input :commenter %>
<%= f.input :body %>
<% end %>
<%= f.button :submit %>
<% end %>
Thanks for the help.

comments validation errors not shown in posts view

I am working on basic blog engine and i have applied validations on comments but when i do a submit it doesn't show errors, instead it shows ActiveRecord::RecordInvalid by rails which is default.
my comments controller is
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create!(params[:comment])
redirect_to #post
end
my posts/show view is as below which is working fine for commenting
<%= form_for [#post, Comment.new] do |f| %>
<p class="comment-notes">Your email address will not be published. Required fields are marked <span class="required">*</span></p>
<p>
<b><%= f.label :name, "Name * " %></b><%= f.text_field :name %><br /></p>
<p>
<b><%= f.label :body, "Comment" %></b><%= f.text_area :comment, :cols => 60, :rows => 5 %>
</p>
<p>
<%= f.submit "Post Comment" %>
</p>
can anybody help me to show validation errors on the same posts/show view?
thanks in advance
replace
#comment = #post.comments.create!(params[:comment])
redirect_to #post
with
#comment = #post.comments.create(params[:comment])
if #comment.errors.any?
render "posts/show"
else
redirect_to #post
end
unlike create, create! will raise error if validations fail
in posts/show
<%= form_for [#post, Comment.new] do |f| %>
<% if #comment && #comment.errors.any? %>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
...
Try this:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new(params[:comment])
if #post.save
redirect_to #post
else
flash[:error] = "Correct errors"
end
end
In Post model:
accepts_nested_attributes_for :comments
or
If you don't want to make as nested model:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new(params[:comment])
if #comment.save
redirect_to #post
else
flash[:error] = "Correct errors"
end
end

Resources