ipsum
ipsum

Reputation: 1052

Rails ajax form entry shown twice

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

Answers (2)

bodacious
bodacious

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

Dennis
Dennis

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

Related Questions