Laurence Laurence - 7 days ago 5
Ajax Question

rails ajax overwriting all partials when refreshing page

I was making a simple variation of the simple example of ajax in rails: http://guides.rubyonrails.org/working_with_javascript_in_rails.html#a-simple-example

I am trying to create a user show view in which new posts are created upon pressing a button. These posts are all listed below the user on the same show page and appear upon creation.

The posts are created fine and shown correctly on the page, however when I refresh the page my

@post = User.posts.build()


Overwrites all the previously created posts giving each of them a nul id.
Also, is it correct to place the create.js.erb in the views folders, or should it go in the assets/javascripts folder?

Here are my files:

UsersController

def show
@user = User.find(params[:id])
@posts = Post.all
@post = @user.posts.build
end


users/show.html.erb

<%= "user-id: #{@user.id}" %>

<ul id="posts">
<%= render :partial => @posts %>
</ul>


<%= form_for(@post, remote: true) do |f| %>
<%= f.hidden_field :user_id, :value => @user.id %>
<%= f.submit %>
<% end %>


PostsController

def create
puts params[:post][:user_id]
@user = User.find(params[:post][:user_id])
puts @user
@post = @user.posts.build()
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.js {}
format.json { render json: @post, status: :created, location: @post }
else
format.html { render action: "new" }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
Rails.logger.info(@post.errors.inspect)
end
end

def index
@posts = Post.all
@post = Post.new
end


posts/_post.html.erb

<li><%= @post.id %></li>


posts/create.js.erb

$("<%= escape_javascript(render @post) %>").appendTo("#posts");

Answer

The Problem

The reason why your partial is rendering all new posts is because you are using the instance variable @post instead of the local variable post inside your partial.

In your UsersController#show action, you set @post = @user.posts.build. When you render <%= @post.id %> inside the partial, you are referencing that same variable which comes from the controller and was set to a new post.

It seems that all the previously created posts are being "null'ed" out, but it's really just that you are continuously rendering a new post, not the existing ones.

The Solution

You need to update your partial to use a local variable, not the instance variable.

<li><%= post.id %></li>

This local variable is automatically provided to you by Rails when you render a collection of records like this

<%= render @user.posts %>

or alternatively

<%= render partial: "posts/post", collection: @user.posts %>

How to Avoid in the Future

This is a really common mistake and can be easy to miss.

For this reason, my recommendation is to stop using instance variables in partials altogether.

Resources