I have one question about form_for with nested resources. I create something like blog, with posts,comments, comments on comments(like replies).And I have an issue. Then I try to make comment it: "Redirected to http://localhost:3000/
Filter chain halted as :get_parent rendered or redirected
Completed 302 Found"
new.html.erb for comments:
<div class= "container" %>
<%= form_for #comment do |f| %>
<%= f.input :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
</div>
My comments controller:
before_filter :get_parent
def new
#comment = #parent.comments.build
end
def create
#comment = #parent.comments.build(params[:comment])
#comment.user_id = current_user.id
if #comment.save
redirect_to posts_path(#comment.post), :notice => 'Thank you for your comment!'
else
render :new
end
end
private
def comment_params
params.require(:comment).permit(:body, :title, :user_id, :commentable_id, :commentable_type)
end
def get_parent
#parent = Post.find_by_id(params[:post_id]) if params[:post_id]
#parent = Comment.find_by_id(params[:comment_id]) if params[:comment_id]
redirect_to root_path unless defined?(#parent)
end
end
post model:
has_many :comments, as: :commentable
belongs_to :user
def post
commentable.is_a?(Post) ? commentable : commentable.post
end
comment model:
belongs_to :user
belongs_to :commentable, polymorphic: true
has_many :comments, :as => :commentable
routes:
resources :posts do
resources :comments
end
resources :comments do
resources :comments
end
post_show.html.erb
<h1><%= #post.title %></h1>
<div class="body">
<%= #post.body %>
</div>
<h2>Comments</h2>
<p><%= link_to 'Add a Comment', new_post_comment_path(#post) %></p>
<ul class="comment_list">
<%= render :partial => 'comments/comment', :collection => #post.comments %>
</ul>
github repo with app: https://github.com/Dmitry96/dasasd
Your new form does not pass neither post_id nor comment_id parameter. It should be eather in the form action url or in the form body.
I can not see all the picture, but I think you have to add parent id to the form action url. It is /comments now, has no parent id parameter in it. It must be /posts/:post_id/comments or /comments/:comment_id/comments.
Change your form to:
<%= form_for [#parent, #comment] do |f| %>
<%= f.input :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
Related
I have a simple blog app that has articles. Each article has comments. I'm trying to build nested comments using the closure_tree gem. I've been loosely following this sitepoint tutorial.
I have the following code:
models/article.rb
class Article < ActiveRecord::Base
has_many :comments
end
models/comment.rb
class Comment < ActiveRecord::Base
acts_as_tree order: 'created_at DESC'
end
routes.rb
resources :articles do
resources :comments
get 'comments/new/(:parent_id)', to: 'comments#new', as: :new_comment
end
views/articles/show.html.erb
<h1><%= #article.title %></h1><br>
<h3><%= #article.body %></h3>
Comments:
<% #article.comments.each do |comment| %>
Title: <%= comment.title %>, Body: <%= comment.body %>, User: <%= comment.user_id %>
<%= link_to 'reply', article_new_comment_path(parent_id: comment.id, article_id: #article.id) %>
<% end %>
</ul>
<!-- FORM FOR NEW COMMENT -->
<%= form_for ([#article, #article.comments.build]) do |f| %>
<%= f.hidden_field :parent_id %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
views/comments/new.html.erb
<%= render "form" %>
views/comments/_form/html.erb
<%= form_for ([#comment.article_id, #article]) do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body %>
<%= f.submit %>
<% end %>
controllers/comments_controller.rb
[...]
def new
#article = Article.find(params[:article_id])
#comment = Comment.new(parent_id: params[:parent_id])
end
def create
# binding.pry
if params[:comment][:parent_id].to_i > 0
parent = Comment.find_by_id(params[:comment].delete(:parent_id))
#comment = parent.children.build(comment_params)
else
#article = Article.find(params[:article_id])
#comment = #article.comments.create(comment_params)
[...]
end
When I click the link_to in articles/show.html.erb in order to reply to an existing comment, I hit the new action as expected and pass the comment's parent_id and article_id also as expected into params.
The problem arises when I leave the new action. I expect to hit the form partial and then go into the create action for a comment. Instead I'm somehow hitting the update action for an Article, even though I don't even have one in my ArticlesController. I'm quite a Rails noob and I think I'm messing up in my nested routes. Any help would be much appreciated.
I try to create an action 'subcomment' or 'child' in the controller. Something like this 'post article/:id/comments/:id/child'.
resources :articles do
resources :comments do
post :child
end
end
In action child something like this.
def child
#article = Article.find(params[:article_id]
#comment = #article.comments.find(params[:comment_id])
#child_comment = #comment.children.build(comment_params)
redirect_to article_path(#article)
end
The view is other problem. Iterate over then articles.comments and comments.children and create a form with the url child_article_comment_path(#article, #comment).
I am building a blog application in which I am trying to have two links in that two links I am sending the post_id to my update controller but it is giving me error .And I want to collect that post_id in my update controller and want to check the "status"(Status is basically the column name in my posts table and by default my status column has a status of pending ) .So when admin click on approve the status of column should change to approve and when admin click on decline the status of column should change to decline also user post should be deleted .Admin can access all the users posts whereas user can access his only post
posts_controller
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post=find_params
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
Post.upload(params[:post][:files],#post.id)
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
puts "cccccccccc#{params[:commit]}"
Post.up(#post.id,params[:commit])
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def find_params
Post.find(params[:id])
end
end
posts/_form.html.erb
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<!-- if you want to upload multiple files at a time -->
<%= f.label :files %><br>
<%= f.file_field :files,:multiple => true %><br>
<br>
<%= f.submit %>
<br>
<% end %>
posts/edit.html.erb
<div id="page_wrapper">
<h1>Edit Post</h1>
<%= render 'form' %>
<br>
</div>
migration
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.integer :user_id
t.string :status
t.timestamps
end
end
end
posts/show.html.erb
<div id="post_content">
<h1 class="title"><%= #post.title %></h1>
<p class="date">
Submitted <%= time_ago_in_words(#post.created_at) %> Ago
<% if user_signed_in? %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Delete', post_path(#post), method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to "approve",[:edit,#post] %>
<%= link_to "decline",[:edit,#post] %>
<% end %>
</p>
<p class="body"><%= #post.body %></p>
<div id="comments">
<h2><%= #post.comments.count %> Comments</h2>
<%= render #post.comments %>
<h3>Add a comment:</h3>
<%= render "comments/form" %>
</div>
</div>
post.rb
class Post < ActiveRecord::Base
has_many :documents
has_many :comments, dependent: :destroy
belongs_to :user
validates :title, presence: true, length: {in: 5..15}
validates :body, presence: true,length: {in: 5..200}
def self.up(id,params)
puts "aaaaaaaa#{id}"
puts "bbbbbbbbbbbbb#{params}"
end
def self.user_post(id)
role = User.find_role(id)
if role == 'admin'
Post.all
elsif role == 'user'
Post.where(user_id: id)
elsif role == 'developer'
Post.where(user_id: id)
end
end
def self.icon(extension)
case extension
when 'pdf'
EXTENSION[1]['pdf']
when 'png' || 'jpg' || 'jpeg'
EXTENSION[0]['png']
when 'doc' || 'odt'
EXTENSION[2]['doc']
end
end
####limit to upload files not more than ######
def self.upload(files,post_id)
files.each do |file|
#file_extension=file.content_type.split('/')[1]
doc = Document.new(document: file,post_id: post_id )
#save is a method which will save the content in the database
doc.save!
end
end
end
routes.rb
Rails.application.routes.draw do
root "posts#index"
devise_for :users
resources :posts do
resources :comments
end
resources :uploads
end
There are a bunch of problems with your code.. I'll not try to address them all but the specific problem you are facing..
first you need to generate path for both approve and decline action..
resources :posts do
patch '/approve' => 'posts#approve', as: :approve #posts_approve_path(post) is the route helper for this
patch '/decline' => 'posts#decline', as: :decline #posts_decline_path(post) is the route helper for this
resources :comments
end
this will generate the required routes.
now inside your view wherever you want to approve and decline.
<%= link_to "approve",posts_approve_path(#post.id), method: :patch %>
<%= link_to "decline",posts_decline_path(#post.id), method: :patch %>
In your controller you need to modify find_params as the generated routes will pass :posts_id as the :id param.
def find_params
id = params[:id] || params[:posts_id]
Post.find(id)
end
Now in your controller add 2 new methods to implement the functionality.
def approve
#post = find_params
#post.update_attribute(:status, 'approved') if #post.present?
redirect_to post_path(#post), notice: 'Post approved'
end
def decline
#post = find_params
#post.destroy if #post.present?
redirect_to post_path(#post), notice: 'Post deleted'
end
I want to create a form for "Comments" route which is a member of Article Resources:
resources :articles do
member do
post 'comments'
end
end
I want the comment form to be in Articles#Show page. The problem i got an error:
First argument in form cannot contain nil or be empty
If the for is like this:
<div>
<%= form_for #comm do |c| %>
<%= c.label :Your_comment %>
<%= c.text_area :commBody %>
<%= c.submit 'submit' %>
<% end %>
</div>
So how to do it ?
If this is your controller,
def show
#article = Article.find(params[:id])
end
and you want to create a form for a new Comment related to #article that points to POST /articles/3/comments:
<%= form_for([#article, Comment.new], as: :article, url: comments_article_path(#article)) do |f| %>
<%= f.label :body %>
<%= f.text_area :body %>
<%= f.submit 'Submit' %>
<% end %>
Don't forget to add accepts_nested_attributes_for :comments in the Article model. And also don't forget to setup the whitelisted params in the ArticleController.
Another thing: don't use abbreviations for your variable names. Use #article and #comment, not #art and #comm.
#config/routes.rb
resources :articles do
post :comment, on: :member #-> url.com/articles/:id/comment
end
#app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
#article = Article.find params[:id]
#comment = #article.comments.new
end
end
#app/views/articles/show.html.erb
Comment:
<%= form_for [#article, #comment], url: article_comment_path(#article) do |c| %>
<%= c.label "Your Comment" %>
<%= c.text_area :commBody %>
<%= c.submit %>
<% end %>
I was following this tutorial http://www.sitepoint.com/nested-comments-rails/ to implement nested comments for an image board. It worked fine until I made "comments" belong to "boards" and then had to nest my routes.
Here are my routes:
Rails.application.routes.draw do
root "boards#index"
devise_for :users do
get '/users/sign_out' => 'devise/sessions#destroy'
end
resources :boards do
resources :comments
get '/comments/new/(:parent_id)', to: 'comments#new', as: :new_comment
get '/comments/(:parent_id)', to: 'comments#destroy', as: :delete_comment
get '/comments/edit/(:parent_id)', to: 'comments#edit', as: :edit_comment
end
end
Here is my form:
<%= form_for [#board, #comment] 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 %>
<%= f.hidden_field :parent_id %>
<div class="form-group">
<% if #comment.parent_id == nil %>
<%= f.label :title %>
<%= f.text_field :title, class: 'form-control' %>
<% else %>
<% nil %>
<% end %>
</div>
<div class="form-group">
<%= f.radio_button(:user_id, current_user.id) %>
<%= f.label(:user_id, "I want to post as myself") %>
<%= f.radio_button(:user_id, nil) %>
<%= f.label(:user_id, "I want to post anonymously") %>
</div>
<div class="form-group">
<%= f.label :content %>
<%= f.text_area :content, class: 'form-control', required: true %>
</div>
<div class="form-group">
<%= f.label :image %>
<%= f.file_field :image %>
</div>
<%= f.submit class: 'btn btn-primary' %>
<% end %>
And here is my controller:
class CommentsController < ApplicationController
def index
#comments = Comment.hash_tree
end
def new
#comment = Comment.new(parent_id: params[:parent_id])
end
def edit
#comment = Comment.find(params[:parent_id])
end
def create
if params[:comment][:parent_id].to_i > 0
parent = Comment.find_by_id(params[:comment].delete(:parent_id))
#comment = parent.children.build(comment_params)
else
#comment = Comment.new(comment_params)
end
if #comment.save
redirect_to root_url
else
render 'new'
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #comment
else
render 'edit'
end
end
def make_parent
#comment.parent_id = nil
#comment.save
end
def destroy
#comment = Comment.find(params[:parent_id])
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url }
end
authorize! :destroy, #comment
end
private
def comment_params
params.require(:comment).permit(:title, :content, :user_id, :image)
end
end
I've tried setting a custom route in the form, this gets the form to at least appear, however when you hit the submit button it returns 'No route matches [POST] "/boards/1/comments/new"'. If I got to the controller and then change the corresponding "get" to a "post" then it causes the form to just reappear after pressing submit and nothing is added to the database. I've also experimented with shallow nesting my routes as per my instructors advice but this didn't work.
Your association between boards and comments must be:
board.rb
has_many :comments
comment.rb
belongs_to :user
routes.rb
resources :boards do
resources :comments, only: [:new, :edit, :destroy]
end
this will create a route
new_board_comment GET /boards/:board_id/comments/new(.:format) comments#new
edit_board_comment GET /boards/:board_id/comments/:id/edit(.:format) comments#edit
board_comment DELETE /boards/:board_id/comments/:id(.:format) comments#destroy
I have a rails blog where all posts are nested under categories and now I am adding comments with are nested under posts but the form is throwing undefined method `post_comments_path' error.
I think I need to make the #posts something like #categories.post but I am unsure.
Routes
resources :categories do
resources :posts, path: 'article' do
resources :comments, :only => [:create]
end
end
Controller
def create
#post = Post.find(params[:post_id])
#comment = #posts.comments.create!(params[:comment])
redirect_to #post
end
View
<%= simple_form_for [#post, Comment.new ], :remote => true do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
I think you're forgetting categories. You need to either provide a category:
Controller
def new
...
#category = Category.find(params[:category_id])
...
end
def create
#post = Post.find(params[:post_id])
#comment = #posts.comments.create!(params[:comment])
redirect_to #post
end
View
<%= simple_form_for [#category, #post, Comment.new ], :url => category_post_comments_path, :remote => true do |f| %>
<%= f.input :name %>
<%= f.input :email %>
<%= f.input :comment %>
<%= f.button :submit %>
<% end %>
or remove categories from the routes like so:
Routes
resources :posts, path: 'article' do
resources :comments, :only => [:create]
end