Reputation: 1052
Hello I want to write a small blog with Ruby on Rails (3), with posts and comments submitted via a ajax form.
But when I submit a comment it is often shown twice, and I got no idea why. when I write @post.comments.uniq in the _create.js.rjs file, it works fine but this seems not to be a clean solution. When I reload the page without ajax after inserting a comment the comment is also not shown twice. Only when I insert it via ajax.
Here is the sourcecode of my project.
Blog::Application.routes.draw do
root :to => 'posts#index'
resources :posts do
resources :comments
end
end
config/routes.rb
ActiveRecord::Schema.define(:version => 20100907105618) do
create_table "comments", :force => true do |t|
t.text "text"
t.integer "post_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "posts", :force => true do |t|
t.string "title"
t.text "text"
t.datetime "created_at"
t.datetime "updated_at"
end
end
db/schema.rb
class Comment < ActiveRecord::Base
belongs_to :post
default_scope :order => "id DESC"
end
app/models/comment.rb
class Post < ActiveRecord::Base
has_many :comments
end
app/models/post.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
end
app/controllers/posts_controller.rb
class CommentsController < ApplicationController
respond_to :js
def create
@post = Post.find(params[:post_id])
# if I write here p @post.comments.inspect
# it shows that there where 2 comments with the same id, how could this be?
@post.comments.create(params[:comment])
end
end
app/controllers/comments_controller.rb
<h2><%= @post.title %></h2>
<p>
<%= @post.text %>
</p>
<%= form_for [@post, Comment.new], :remote => true do |f| %>
<%= f.text_area :text, :rows => 4 %><br />
<%= f.submit "send" %>
<% end %>
<div id="comments_box">
<% if @post.comments.any? %>
<%= render :partial => @post.comments %>
<% else %>
No Comments yet
<% end %>
</div>
app/views/posts/show.html.erb
<div id="comment_<%= comment.id %>"><%= comment.text %></div>
app/views/comments/_comment.html.erb
page[:comment_text].clear
page[:comments_box].replace_html :partial => @post.comments
# ^ write here @post.comments.uniq it works
page.visual_effect(:highlight, "comment_#{@post.comments.first.id}")
app/views/comments/create.js.rjs
<% @posts.each do |post| %>
<%= link_to post.title, post %>
<% end %>
app/views/posts/index.html.erb
EDIT:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
<%= csrf_meta_tag %>
</head>
<body>
<%= yield %>
app/views/layouts/application.html.erb
Upvotes: 2
Views: 758
Reputation: 6705
I believe what's happening here is...
when you call
@post.comments.create(params[:comment])
Rails appends a new comment to the post. Then, when calling
:partial => @post.comments
Rails will call all of the comments from the DB that belong to this post.
We can see this in the log:
SQL (0.5ms) INSERT INTO "comments" ("created_at", "post_id", "text", "updated_at") VALUES ('2010-09-09 11:10:18.874471', 1, 'lots of pies', '2010-09-09 11:10:18.874471')
Comment Load (0.9ms) SELECT "comments".* FROM "comments" WHERE ("comments".post_id = 1) ORDER BY id DESC
Instead, try creating a new comment and then saving like so:
class CommentsController < ApplicationController
respond_to :js
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.new(params[:comment])
@comment.save
end
end
And in the view:
page[:comment_text].clear
page[:comments_box].replace_html render(@post.comments)
page.visual_effect(:highlight, "comment_#{@post.comments.first.id}")
I've posted this as an example to Github http://github.com/GavinM/Comments-Demo
Upvotes: 0
Reputation: 306
I'm still not a profi in rails but you can check which js you link to your application layout file. I used to link once defaults and after my application.js and received all the ajax action twice. However I'm not sure about your case. The code you pasted looks fine.
Upvotes: 0