nested edit path not working - ruby-on-rails

Trying to figure out the edit path within _comment.html.erb file.
Keep getting this error:
ActiveRecord::RecordNotFound in CommentsController#edit
Couldn't find Article with 'id'=#<Comment::ActiveRecord_Associations_CollectionProxy:0x007fcac37359e8>
Don't know how to figure how to write the correct path.
Comments Controller
class CommentsController < ApplicationController
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
end
def show
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
def edit
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
def destroy
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to article_path(#article)
end
private
def comment_params
params.require(:comment).permit(:commenter, :body)
end
end
_comment.html.erb
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<p>
<%= link_to 'Edit', edit_article_comment_path(#article.comments, comment) %>
</p>
<p>
<%= link_to 'Show', [comment.article, comment] %>
</p>
<p>
<%= link_to 'Destroy Comment', [comment.article, comment],
method: :delete,
data: { confirm: 'Are you sure?' } %>
</p>
routes
resources :articles do
resources :comments
end
How do I write the correct path?
Also, the earlier path I had was this:
<%= link_to 'Edit', edit_article_comment_path(#article, comment) %>
But it would turn up a blank edit form, ie, none of the text boxes had anything filled in ... hence why I tried the other path.
Any help would be appreciated.
Thank you.
Comment _form.html.erb
<%= form_for([#article, #article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Article show.html.erb
<p>
<strong>Title:</strong>
<%= #article.title %>
</p>
<p>
<strong>Text:</strong>
<%= #article.text %>
</p>
<h2>Comments</h2>
<%= render #article.comments %>
<h2>Add a comment:</h2>
<%= render 'comments/form' %>
<%= link_to 'Edit', edit_article_path(#article) %> |
<%= link_to 'Back', articles_path %>

There's a problem in _comment.html.erb
<%= link_to 'Edit', edit_article_comment_path(#article.comments, comment) %>
It should be
<%= link_to 'Edit', edit_article_comment_path(#article, comment) %>
But it would turn up a blank edit form, ie, none of the text boxes had anything filled in
You should check if you are passing the proper values in form_for
<%= form_for [#article, #comment] do |f| %>
EDIT2
Well the problem is you are passing new instance of comment every time, Even with the edit action. That's why you are not getting any values in the form
<%= form_for([#article, #article.comments.build]) do |f| %>
Change it to
<%= form_for([#article, #comment]) do |f| %>
And make sure you are assigning #article and #comment in both new and edit action of CommentsController or from wherever you are rendering the comments/_form
class CommentsController < ApplicationController
def new
#article = Article.find(params[:article_id])
#comment = #article.comments.new
end
def edit
#article = Article.find(params[:article_id])
#comment = #article.comments.find(params[:id])
end
#...
end

Related

ajax rails form_with submits but only after page reload

I've had an existing comment form on a resource show.html.erb that I want to be added to the show page without having to reload the entire page. I understand that with ajax via the 'form_with' form, this should be entirely possible.
My issue is that whilst the form does create a new comment, it only appears after I refresh the page.
I also get the following error.
ArgumentError (too few arguments):
app/controllers/comments_controller.rb:18:in 'format'
app/controllers/comments_controller.rb:18:in 'create'
comments_controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :authenticate_user!
before_action :comment_auth, only: [:edit, :update, :destroy]
...
def create
#comment = #commentable.comments.new(allowed_params)
#comment.user_id=current_user.id if current_user
if #comment.save
format.html { redirect_to #commentable, notice: "Comment added." }
format.js
else
render 'edit'
end
...
views/comments/create.js.coffee
comment = document.getElementById("comment")
comment.innerHTML = "<%= j render(#comment) %>"
views/comments/_form.html.erb
<%= form_with(model: [#commentable, #comment]) do |f| %>
<% if #comment.errors.any? %>
<div class="error_messages">
<h2>Please correct the following errors.</h2>
<ul>
<% #comment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<strong><%= f.label :name, :class=>'form-label' %></strong><br>
<%= f.text_field :name, value: current_user.fname, readonly: true, :class=>'form-control' %>
</p>
<p>
<strong><%= f.label :title, :class=>'form-label' %></strong><br>
<%= f.text_field :title, :class=>'form-control' %>
</p>
<p>
<strong><%= f.label :review, :class=>'form-label' %></strong><br>
<%= f.text_area :body, :class=>'form-control', rows: 5 %>
</p>
<p>
<%= f.submit :class=>'btn btn-success' %>
</p>
<% end %>
For me, I needed
<%= form_with(model: #message, method: :post, local: true) do |f| %>
local: true was necessary. All other combos didn't work remote: true, remote: false, local: false
as you know, form_with it's a remote form, you should not use redirect_to there. Please remove your block
if #comment.save
format.html { redirect_to #commentable, notice: "Comment added." }
format.js
else
render 'edit'
end
replace it to
if #comment.save
#status = "success"
end
And you can add a notice message to your views/comments/create.js.erb (it's not .js.coffee) with condition of #status (failed/success).
Hope this helps.

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? %>

Incorrect path in form_for helper

I can't figure out how to correctly write a url path in form_for for creating a new object. I tried to do it different ways, but no luck. I suppose there is some particular form of url's that can include objects' id's.
views/tasks/new.html
<%= form_for :task, url: [#task.user, #task] do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.hidden_field :user_id, value: params[:user_id] %>
<%= f.submit %>
</p>
<% end %>
rake routes
controllers/tasks_controller.rb
class TasksController < ApplicationController
before_filter :authorize, only: [:edit, :new, :destroy]
def index
#tasks = Task.where(user_id: params[:user_id])
end
def show
#task = Task.find(params[:id])
redirect_to users_path
end
def edit
#task = Task.find(params[:id])
end
def new
#task = Task.new
end
def create
#task = Task.new(task_params)
if #task.save
redirect_to #task
else
render 'new'
end
end
def update
#task = Task.find(params[:id])
if #task.update(task_params)
redirect_to #task
else
render 'edit'
end
end
def destroy
#task = Task.find(params[:id])
#task.destroy
respond_to {|format| format.js }
end
private
def task_params
params.require(:task).permit(:title, :description)
end
end
views/tasks/index.html
<p align="right"><%= link_to 'Users', users_path %> <%= link_to 'Tasks', user_tasks_path %></p>
<h3>Tasks database</h3>
<table>
<% #tasks.each do |task| %>
<tr id="task_<%= task.id %>">
<td>
<b>Title:</b>
<i> <%= task.title %></i>
</td>
<td>
<b>Description: </b>
<i><%= task.description %></i>
</td>
<td>
<%= link_to 'Edit', edit_user_task_path(task.user, id: task.id) %>
</td>
<td>
<%= link_to 'Delete', user_task_path(task.user, id: task.id),
data: { confirm: 'Are you sure?' }, :method => :delete, remote: true %>
</td>
</tr>
<% end %>
<tr>
<td><%= link_to 'Create task', new_user_task_path %></td>
<td><%= link_to 'Back to Users', users_path %></td>
</tr>
</table>
Can anybody help?
You have nested_routes for tasks and users, so changing your form_for like below should solve your problem
<%= form_for [current_user, #task] do |f| %>
Please can you try this:
<%= form_for([current_user, #task]) do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.hidden_field :user_id, value: params[:user_id] %>
<%= f.submit %>
</p>
Seeing the routes it seems that you need to allow either the admin/user to create tasks for any user. So for this first add a before_action in your controller:
before_action :find_user
def find_user
#user = User.find(params[:user_id])
end
Then in you form do it like:
<%= form_for [#user, #task] do |f| %>
Then in the create action of your controller do it like:
def create
#task = #user.tasks.build(task_params)
if #task.save
redirect_to #task
else
render 'new'
end
end
The above can be used when you need to save tasks for many user and if you need that only the current_user tasks should be saved you can go with the above answers.

Rails Guide "undefined method 'comments' for nil:NilClass"

I am new to Rails and working through the Getting Started Guide. I read through this similar question about the guide but it doesn't seem relevant.
I am stuck on section 6.3, where we are trying to let users add comments to blog posts. I have added a comments form to the post show view, which was working fine before, but now raises the following error. What is the issue?
NoMethodError in Posts#show
Showing /Users/.../Desktop/Rails Blog/blog/app/views/posts/show.html.erb where line #24 raised:
undefined method `comments' for nil:NilClass
Extracted source (around line #24):
21 <% end %>
22
23 <h2>Add a comment:</h2>
24 <%= form_for([#post, #posts.comments.build]) do |f| %>
25 <p>
26 <%= f.label :commenter %><br />
27 <%= f.text_field :commenter %>
posts_controller.rb:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(params[:post].permit(:title, :text))
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :text))
redirect_to #post
else
render 'edit'
end
end
def show
#post = Post.find(params[:id])
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :text)
end
end
comments_controller:
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:commenter, :body))
redirect_to post
end
end
posts.show.html.erb:
<p>
<strong>Title:</strong>
<%= #post.title %>
</p>
<p>
<strong>Text:</strong>
<%= #post.text %>
</p>
<h2>Comments</h2>
<% #post.comments.each do |comment| %>
<p>
<strong>Commenter:</strong>
<%= comment.commenter %>
</p>
<p>
<strong>Comment:</strong>
<%= comment.body %>
</p>
<% end %>
<h2>Add a comment:</h2>
<%= form_for([#post, #posts.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br />
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', posts_path %>
<%= link_to 'Edit', edit_post_path(#post) %>
You have #posts. The variable is called #post. Drop the "s".

What variable should I place in this form to edit a comment (Rails)?

I got two models: Post and Comment. Comment is a nested resource of Post:
routes.rb:
resources :posts do
resources :comments
end
I'm enabling users to edit the comments that are displayed in the post show view:
posts/show.hmtl.erb:
<%= render #comments %>
comments/_comment.html.erb:
<%= link_to "Edit Post Comment", edit_post_comment_path(#post, comment) %>
This form:
comments/_form.html.erb:
<h4>Add a comment:</h4>
<%= form_for([#post, #post.comments.build]) do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% unless current_user == nil %>
<% if current_user.id == #post.user_id %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<% end %>
<% end %>
<%= link_to 'Back', posts_path %>
Is for creating a comment in the show view.
I need another form to edit the comment in a new template:
comments/edit.html.erb:
<h1>Edit comment</h1>
<%= render 'form2' %>
<%= link_to 'Back', posts_path %>
comments/form2.html.erb:
<h4>Edit comment:</h4>
<%= form_for() do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<%= link_to 'Back', posts_path %>
Not sure what to place here:
<%= form_for(HERE) do |f| %>
Any suggestions?
EDIT
comments_controller.rb:
class CommentsController < ApplicationController
def show
#comment = Comment.find(params[:id])
end
def new
#comment = Comment.new
end
def edit
#comment = Comment.find(params[:id])
end
def update
#post = Post.find(params[:post_id])
#comment = Comment.find(params[:id])
#comment.update_attributes(params[:comment])
redirect_to #post
end
def create
#post = Post.find(params[:post_id])
comment_attr = params[:comment].merge :user_id => current_user.id
#comment = #post.comments.create(comment_attr)
redirect_to post_path(#post)
end
end
Comments are nested resources and that means a comment is always tied to a particular Post. In the edit action you'll get both comment_id and post_id as parameters. You should load both comment and post by those id's. Don't build the comment in the form to reuse the form for both new and edit actions. Change your form to:
comments/_form.html.erb
<%= form_for([#post, #comment]) do |f| %>
comments_controller.rb
def edit
#comment = Comment.find(params[:id])
#post = Post.find(params[:post_id])
end
and in the posts_controller.rb
def show
#post = Post.find(params[:id])
#comment = #post.comments.build
end

Resources