I am trying to destroy a comment through Ajax. In my app, my comment model uses a polymorphic association. The comment deletes successfully (in the database) using the code below. However, when I call the destroy.js.erb, it doesn't do anything and I think the problem is that the dom_id doesn't match the HTML id, so it is not updating. I think I am experiencing the same thing that #mu_is_too_short articulated in the answer to this question. I need help with how to solve this though. I do not know if the solution involves a) somehow passing the local variable comment to the destory.js.erb or b) another solution.
routes.rb
resources :feeds do
resources :comments
end
destroy.js.erb
$('#<%= dom_id(#comment) %>')
.fadeOut ->
$(this).remove()
_comment.html.erb
<div id=<%= dom_id(comment) %> class="comment">
<em>on <%= comment.created_at.strftime('%b %d, %Y at %I:%M %p') %></em>
<%= link_to "Remove", [#commentable, comment], :method => :delete, :remote => true %>
<%= simple_format comment.content %>
</div>
feeds/show.html.erb
<div id="comments">
<%= render #comments %>
</div>
Comments controller
class CommentsController < ApplicationController
def create
#comment = #commentable.comments.new(params[:comment])
if #comment.save
respond_to do |format|
format.html { redirect_to #commentable }
format.js
end
else
render :new
end
end
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
respond_to do |format|
format.html { redirect_to #commentable }
format.js
end
end
end
end
Feeds controller
class FeedsController < ApplicationController
def show
#feed = Feed.find(params[:id])
#commentable = #feed
#comments = #commentable.comments
#comment = Comment.new
end
end
By the looks of it, #comment will contain a comment when you get to destroy.js.erb, as you set it in the controller immediately before that, so I do not think it has anything to do with the referenced answer. I wonder if this is being caused by the lack of quotes around your id here: <div id=<%= dom_id(comment) %> class="comment">... I suspect if you correct this, and any other relevant HTML validation errors, it will start working.
Related
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");
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
Rails Guides - Rendering Collections
Stop Using Instance Variables In Partials
Found a workaround by avoiding the use of the _post partial to display the older posts:
edit: users/show.html.erb
<ul id="posts">
<% #posts.each do |post| %>
<li>
<%= post.id %>
</li>
<% end %>
</ul>
I don't think this is an ideal solution but it works fine for now.
Yes, you are correct.
create.js.erb with the suffix erb to help you add code ruby to it.
Write code in this file the same with you write javascript code on view html.erb with syntax
<script>
$("<%= escape_javascript(render #post) %>").appendTo("#posts");
</script>
I've a users list and I want to delete elements on click of a "delete" link on the same line.
Views:
# users/index.html.erb
<ul class="users">
<%= render #users %>
</ul>
# users/_user.html.erb
<li id="user-<%= user.id %>">
<%= link_to user.email, user %>
<% if current_user.admin? %>
<%= link_to "delete", user, remote: true, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
</li>
Users Controller:
# users_controller.rb
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
respond_to do |format|
format.html { redirect_to users_url }
format.js
end
end
The javascript to delete the list element is:
# users/destroy.js.erb
$("li#<%= user.id %>").remove();
On click nothing happen, but on page refresh the resource is correctly destroyed. Checking server's LOG I see the following error:
Completed 500 Internal Server Error in 40ms (ActiveRecord: 5.0ms)
ActionView::Template::Error (undefined local variable or method user'
for #<#<Class:0x0000000851d300>:0x00000008e8e7e0>):
1: $("li#<%= user.id %>").remove(); app/views/users/destroy.js.erb:1:in
_app_views_users_destroy_js_erb___2066095411338793994_74738360'
app/controllers/users_controller.rb:46:in `destroy'
Can you pls help me to understand where the error is and why 'user' is undefined? Thanks
edit
I know there are many q&a similar to this question, and I actually build my code consulting these questions. Still, I'm stuck and I need support. Pls do not mark the question as duplicate.
your destroy.js.erb template is not getting the user variable, as it is undefined. try:
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.js
end
end
and
$("li#<%= #user.id %>").remove();
the reason this works is because rails passes data from controller to view by setting variables with # prefix. so you set #user to an instance of the model you grabbed from the database, then you called .destroy on that model, and it got deleted, and then you rendered your destroy.js template which had #user in it as well, and you deleted the list item matching the 'li#233' selector or whatever the id integer was
You're not defining an user variable.
You would save your #user_id in a variable to be accessible in your js.erb. Like that:
def destroy
#user_id = params[:id]
...
end
And then:
$("li#<%= #user_id %>").remove();
Long story short, I have a button. On clicking it, I want an ajax request to be triggered which gets flash[:notice] and displays it in a div in$
Here is my shortened view:
<input type="button" id="search" value="display"/>
<div id="notice">
</div>
My ajax request in the view:
$("#search").submit(function(){
$.ajax({
type: "POST",
url: //url to my show action
success: function(data){
/*$("#notice").html("<%= flash[:notice] %>");
$("#content").html(data);*/
}
});
return false;
});
My controller:
def HomeController < ActionController::Base
def index
end
def show
respond_to do |format|
format.js { flash[:notice] = "" + count.to_s + " results found for " + params[:query][:search_key] + "" }
end
#render :partial => 'search'
end
end
My show.js.erb
#app/views/dashboard_home/show.js.erb
$("#notice").html("<%=j flash[:notice] %>");
$("#content").html("<%=j render partial: "search" %>");
The problem is when I click on button, the notice is displayed fine. But the same notice persists on the next clicks too. The search partial contains the table Please help!
Here is an example that I got working, thanks to Rich Peck's answer. I needed to use flash.now to make sure the flash notice didn't persist.
AJAX trigger in the view:
<%= link_to "Email report", users_path, remote: true %>
Controller:
# app/controllers/users_controller
class UsersController < ApplicationController
def index
# do some things here
respond_to do |format|
format.js { flash.now[:notice] = "Here is my flash notice" }
end
end
end
Rendered view:
# app/views/users/index.js.erb
$("#flash").html('<%= j render partial: "shared/notice_banner" %>');
where the flash notice is displayed in the layout:
# app/views/layouts/application.html.erb
<div id="flash">
<% if notice.present? %>
<%= render partial: "shared/notice_banner" %>
<% end %>
</div>
# app/views/shared/_notice_banner.html.erb
<div data-alert class="alert-box">
<%= notice %>
×
</div>
Sessions
the same notice persists on the next clicks too
This is caused by the flash being stored in the session variable of Rails:
The flash is a special part of the session which is cleared with each
request. This means that values stored there will only be available in
the next request, which is useful for passing error messages etc.
The problem you have is that since I don't think ajax counts as a new request (need reference for this), the data will persist into the next time you request via HTTP.
--
Fix
I would initially try this:
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
The main problem you have is you're processing the flash variable in your JS using the ERB preprocessor. This is an issue as it means you won't be able to use asset precompile to help it work.
After looking at this question, why not try using the after_filter callback, like this:
#app/controllers/home_controller.rb
Class Home < ActionController::Base
after_filter { flash.discard if request.xhr? }, only: :show
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
end
--
Update
You should include the success functionality in your show.js.erb:
#app/views/home/show.js.erb
$("#notice").html("<%= flash[:notice] %>");
This means you can remove the whole ajax call from the application.js, and replace with the remote: true for your search form:
#app/views/search/index.html.erb
<%= form_tag home_show_path, remote: true %>
The reason this works is because when you use the format.js respond block, Rails will load the [action].js.erb file in your views. Considering this only happens after the action has been completed, it's equivalent to the success function of your ajax.
By doing this, you'll be able to remove the entire ajax function from your application.js, and replace with the UJS version, as described above
I have a strange problem. I have 2 models Issue, Comment. Comments is nested inside Issues so for that I have the create action in comments controller as follows:
def create
#issue = Issue.find(params[:issue_id])
#comment = #issue.comments.create!(params[:comment])
respond_to do |format|
if #comment.save
format.html { redirect_to #comment, notice: 'Comment was successfully created.' }
format.json { render json: #comment, status: :created, location: #comment }
format.js #create.js.erb
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
And my create.js.erb:
var new_comment = $("<%= escape_javascript(render(:partial => #comment))%>").hide();
$('#comments').prepend(new_comment);
$('#comment_<%= #comment.id %>').fadeIn('slow');
$('#new_comment')[0].reset();
Issue.rb
class Issue < ActiveRecord::Base
attr_accessible :category, :description, :title
has_many :comments
end
Comment.rb
class Comment < ActiveRecord::Base
attr_accessible :body, :issue_id
belongs_to :issue
end
routes.rb
resources :comments
resources :issues do
resources :comments
end
Problem: When I create a comment for which is a form partial residing on views/issues/show.html.erb. The comment gets created 4 times in the db.
I couldn't locate what the problem was and whats causing it. Please help
First, I would build the associated comment:
#comment = #issue.comments.build(params[:comment])
And then I would save the comment instance
#comment.save
And also check the Javascript, maybe you are having some problems with event bubbling and the event is being triggered twice.
I actually was working on some old Rails version where the js files were put inside the /public/assets and that was the reason for that weird behaviour. I deleted all the files inside the /public/assets folder and the app works fine now.
I'm trying to show a flash message and render it with Ajax, but the message does not seem to be appearing until after I refresh the page.
Here's my create.rjs file:
page.insert_html :top, :feed_items, :partial => 'shared/feed_item', :object => #micropost
page.replace_html :user_info, pluralize(current_user.microposts.count, "micropost")
page[:micropost_form].reset
page.replace_html :notice, flash[:notice]
flash.discard
Here's the relevant part of my application layout view:
<div id= "notice"><% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %></div>
And here's the relevant part of my micropost controller:
class MicropostsController < ApplicationController
before_filter :authenticate, :only => [:create, :destroy]
before_filter :authorized_user, :only => :destroy
def create
#micropost = current_user.microposts.build(params[:micropost])
respond_to do |format|
if #micropost.save
flash[:success] = "Micropost created!"
format.html { redirect_to root_path }
format.js
else
#feed_items = []
render 'pages/home'
end
end
end
So why isn't the flash message showing up right away?
Flash messages by design are stored in the session until the next page load. In the case of ajax, don't use flash. For one thing, the div to replace the message into may not exist, so you need to add that to your page as a container to add to. Second, just do it from a regular variable, not flash.
A regular variable would be:
#message = "Micropost created!"
instead of the flash[:success]
The js view would use this variable instead of flash.
Instead of page.replace_html :notice, flash[:notice]
Try this page.replace_html :notice, "Some info to flash"