Ruby On Rails: Ajax to Refresh Partial Not Working - ruby-on-rails

I am having a difficult time trying to get my partial to refresh on button press. I want to simply refresh the div, and not the whole page. Here is what I have in the view:
<div id="ajax">
<%= render 'layouts/confessions' %>
</div>
Then in my partial _confessions.html.erb, I have some basic html and two buttons similar to this:
<%= form_tag( { :controller => :confessions, :action => :upvote, :id => conf.id }, { :method => :put } ) do %>
<%= submit_tag 'Like' %>
<% end %>
My confessions_controller.rb:
def upvote
#confession = Confession.find(params[:id])
Confession.increment_counter :upvotes, #confession
respond_to do |format|
format.js
end
end
And finally, upvote.js.erb:
$('#ajax').html("<%= escape_javascript(render(:partial => 'confessions')).html_safe %>");
The action of submitting to my database is working, but the page is now redirecting to /upvote?id=9 (id can be different), instead of just refreshing the div. What am I doing wrong? I am new to Rails, so I could be missing something completely obvious...
EDIT: Here is my folder structure:
My view: views/pages/home.html.erb
My partial: views/layouts/_confessions.html.erb
My Controller: controllers/confessions_controller.rb
My js.erb file: views/confessions/upvote.js.erb
After rake routes
confessions GET /confessions(.:format) confessions#index
POST /confessions(.:format) confessions#create
new_confession GET /confessions/new(.:format) confessions#new
edit_confession GET /confessions/:id/edit(.:format) confessions#edit
confession GET /confessions/:id(.:format) confessions#show
PUT /confessions/:id(.:format) confessions#update
DELETE /confessions/:id(.:format) confessions#destroy
upvote /upvote(.:format) confessions#upvote
downvote /downvote(.:format) confessions#downvote
root / pages#home

Make sure you have this in your application.js
//= require jquery
//= require jquery_ujs
FYI: Rails form_for :remote=>true is not calling js method
Then, change this,
$('#ajax').html("<%= escape_javascript(render(:partial => 'confessions')).html_safe %>");
To:
$('#ajax').html("<%= escape_javascript(render(:partial => 'layouts/confessions')).html_safe %>");

Add :remote => true
<%= form_tag( { :controller => :confessions, :action => :upvote, :id => conf.id }, { :method => :put, :remote=>true } ) do %>
Also - read up on rails routes to see how to setup the route for PUT confessions/upvote.
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions

Related

Rails - Trouble Using Ajax on Like Action

I'm having a little trouble with using AJAX on something I've done before with no issues, but this site has a few different configurations which are causing trouble for me. The main problem I'm having, is when I click the Like link on a post, all of the like links on that page are changed to unlike, even though the one I clicked is the only one getting posted in the Database.
On my user's profile page (users/show.html.erb), I'm showing all of the posts (updates in this scenario) that the particular user has posted.
Here is my configuration thus far:
Users Controller, Show Action
def show
#updates = #user.updates.paginate(:page => params[:page], :per_page => 20)
end
Updates Controller, Like Action
def like
begin
#vote = current_user.vote_for(#update = Update.find(params[:id]))
if #vote.save
respond_with #update.user, :location => profile_path(#update.user)
end
rescue ActiveRecord::RecordInvalid
redirect_to #update
end
end
users/show.html.erb
<div class="like_button">
<%= render :partial => 'updates/like_button', :locals => {:update => update} %>
</div>
updates/_like_button.html.erb Partial
<% if current_user.voted_on?(update) %>
<%= link_to unlike_update_path(update), :method => :post, :remote => true do %>
<i class="ss-heart liked" title="Unlike Update"></i>
<% end %>
<% else %>
<%= link_to like_update_path(update), :method => :post, :remote => true do %>
<i class="ss-heart animate" title="Like Update"></i>
<% end %>
<% end %>
updates/like.js.coffee
$(".like_button").html("<%= escape_javascript render :partial => 'updates/like_button', :locals => {:update => #update} %>");
So just to refresh, the like button works and records the vote into the database, but it changes all of the like_buttons on the page to appear as if they have been liked instead of just the one post.
You need some way to distinguish your like buttons (by css class / id for example) and then you can alter them with jQuery concretely
$(".like_button").html("<%= escape_javas...
while currently $(".like_button") aims for all elements with that .like_button class (which are all).

Pass Rails variable from controller to JS

Problem description:
I have a view with set of links:
<% #feeds.each do |f| %>
<div class="feed">
<span class="feed_counts"> <%= f.display_counts %> </span>
<%= link_to "refresh", { :controller => 'Feeds', :action => "refresh_feed", :feed_id => f.id}, :remote => true, :class => 'refresh_feed_link' %>
</div>
<% end %>
Users click on the link and launch next controller method:
def refresh_feed
#feed = Feed.find(params[:feed_id])
#feed.parse
end
Now I want to change the content of the corresponding span-element to #feed.total_count value.
My attempts:
Well, as I know there is a two way's to do it without reloading whole the page:
Way 1:
I can include js in my layout:
<%= render :partial => 'shared/partial_js' %>
and use this code in the partial_js:
<script type="text/javascript">
$().ready(function() {
$('.refresh_feed_link').bind('click', function() {
$(this).closest('.feed').find('span.feed_counts').text('woo');
});
});
</script>
In this case I have '$(this)' and I can find the corresponding 'span' element. But I don't have any possibility to get my #feed varible value.
Way 2:
I can add
respond_to do | format |
format.js {render :layout => false}
end
to my controller and create refresh_feed.js.erb file. In this JS file I can use my variable as <% #feed.total_count %>, but I don't know which of my multiple links was clicked. In the other words the $(this) variable in this file will be (window) and I cannot find corresponding span-element.
Question:
Is there any way to get what I want ?
Thanks in advance.
There are lots of ways to do this. Using the wayds that you described, here's a simple solution for the "which link was clicked" problem: dom_id.
1) Make a partial: app/views/feeds/_feed.html.erb
<%= div_for feed do %>
<span class="feed_counts"> <%= feed.display_counts %> </span>
<%= link_to "refresh", { :controller => 'Feeds', :action => "refresh_feed", :feed_id => feed.id}, :remote => true, :class => 'refresh_feed_link' %>
<% end %>
2) In your view:
<%= render #feeds %>
3) In your refresh_feed.js.erb file:
$('<%= dom_id(#feed) %>').replaceWith('<%= escape_javascript( render #feed ) %>');
There's another way that I personally like better, but it will take a me a little while to write it up, so I'll leave this for you here while I write up the other way.
Second Way
Here's how I do it, using CoffeeScript and HAML because they're easier to type. You can just convert this to plain JS and ERB and it will work the same.
I would setup my routes like so:
resources :feeds do
get "refresh_feed", :on => :member
Assuming you've got a "feed" partial, app/views/feeds/_feed.html.haml:
= div_for feed, :class => 'feed_widget' do
%span.feed_counts= f.display_counts
= link_to "Refresh", refresh_feed_path(f), :class => 'refresh_link'
In any view:
= render #feeds
// or, more explicit:
= render :partial => 'feed/feeds', :collection => #feeds, :as => :feed
Now, in app/assets/javascripts/feeds.js.coffee
# Global Scope for CoffeesScript, ignore for JS
root = exports ? this
# Check page if there are feed widgets on it and initialize a handler for each one.
jQuery ->
if $('div.feed_widget').length
$('div.feed_widget').each ->
new FeedWidget $(this)
root.FeedWidget = (container) ->
#container = container
#refresh_link = #container.find('a.refresh_link')
#feed_counts = #container.find('span.feed_counts')
this.initialize()
root.FeedWidget.prototype =
initialize: ->
self = this
#feed_counts.click (event) ->
event.preventDefault()
$.ajax
type: 'GET'
url: self.refresh_link.attr 'href'
dataType: 'json'
error: (xhr, status, error) ->
console.log error
console.log xhr
success: (data, status, xhr) ->
self.feed_counts.text data.feed_counts
Then in your controller:
def refresh_feed
#feed = Feed.find(params[:id]) #assuming you have a resourceful route to this.
#feed.parse
respond_to do |format|
format.js { render :json => { feed_counts: #feed.counts }, :status => :ok } # use whatever method gets you the feed count here.
end
end
Now you are getting a JSON response that is just the feed count, and you have a single JS listener/handler widget that will automatically show up (and function) and place you render that partial. Cool huh?
Note, the above code is not tested since I don't have your app, so you'll have to refine it for your needs. But ask questions and I'll do my best to answer.
Good luck!

How do you recreate a remote link with the same .js update in Ruby on Rails?

Basically I want to make a link able to be refreshed dynamically from "Favorited" to "Removed", and at the same time give the user the option to go go back by reclicking the new button. The action does happen though, because when I refresh the page the updated button shows. Why does clicking the "Favorite this Classroom" link not work? The "Remove this Classroom" link seems to work. Thanks for your help
favorites_controller.rb:
def create
current_classroom.add_to_favorites(#classroom)
current_classroom.save
respond_to do |format|
format.js { }
format.html {redirect_to #classroom}
end
end
def destroy
current_classroom.remove_from_favorites(#classroom)
current_classroom.save
respond_to do |format|
format.js { }
format.html {redirect_to #classroom}
end
end
favorites/create.js.erb
$("#favorite_classroom").html("<%= escape_javascript(link_to 'Remove the Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete) %>");
classrooms/_classroom_details.html.erb
<div id="favorite_classroom">
<% if loggedin_user.favorite_classroom?(#classroom) %>
<%= link_to 'Remove this Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete %>
<% else %>
<%= link_to 'Favorite this Classroom', classroom_favorites_path(#classroom), :remote => true, :method => :post %>
<% end %>
routes:
classroom_favorites POST /classrooms/:classroom_id/favorites(.:format) {:action=>"create", :controller=>"favorites"}
classroom_favorite DELETE /classrooms/:classroom_id/favorites/:id(.:format) {:action=>"destroy", :controller=>"favorites"}
Error when I click on the "Favorite this Classroom" link:
ActionView::Template::Error (No route matches {:action=>"destroy", :controller=>"favorites"
Thanks!
You are using double-quotes twice in your js file. You can't do that. You need to rewrite it like this -
$("#favorite_classroom").html("<%= escape_javascript(link_to 'Remove the Classroom', classroom_favorite_path(#classroom), :remote => true, :method => :delete) %>");
Notice now that Remove the Classroom is enclosed in single quotes rather than double.
In addition, your route is incorrect because you have nested favorites within classrooms. When you write the link, you need to add the #favorite object that you are trying to delete:
= link_to 'Remove Favorite', classroom_favorite_path([#classroom, #favorite]), :remote => true, :method => :delete
Now the route you are trying to access is valid. That should get rid of the error.
I don't think you need the favorite controller, what you need is favorite/unfavorite action for classroom controller. Here's how it might look
classroom_controller.rb
respond_to :html, :js
def favorite
# ... find classroom by id
# do your stuff
#classroom.favorite = !#classroom.favorite
respond_with(#classroom)
end
views/classrooms/favorite.js.erb
<%# the message should be oposite to favorite %>
<% msg = #classroom.favorite ? "Remove this Classroom" : "Favorite this Classroom" %>
$("#favorite_classroom").html("<%= escape_javascript(link_to msg, [:favorite, #classroom], :remote => true) %>");
Here's the trick with path helper, [:favorite, #classroom] should be transleted to /classrooms/:id/favorite. If it won't work then try favorite_classrooms_path(#classroom)
And finally add to your routes.rb
resource :classrooms do
member { get :favorite }
end
Looks like you need to reference 'favorite_id' in your link to match your routes.
In your case, would probably be:
classroom_favorite_path([current_classroom, #classroom])

Link_to remote => true causing strange routing problems, Rails 3

I am having some problems with link_to remote sending a post to another controller... The result are not quite what I expect..
I have this in node_content.html.erb:
<% #node.videos.each do |vid| %>
<div id="vid_vid"><%= link_to image_tag("http://img.youtube.com/vi/#{vid.content}/1.jpg"), :controller => 'videos', :action => "iframize", :video_id => vid.id, :method => :post, :remote => true %></div>
<% end %>
And I have this in videos_controller:
def iframize
#video = Video.find(params[:video_id])
respond_to do |format|
format.js
end
end
And this in routes:
resource :videos do
collection do
post 'iframize'
end
end
Problem is that when I click the link, it takes me to
http://localhost:3000/videos/iframize?method=post&video_id=20
and I get
Couldn't find Video with id=iframize
I looked through tens of various examples and they seem to recommend the above, but it does not work.. What am I doing wrong?
Any input greatly appreciated!
Thanks!
EDIT:
I tried this approach jquery function and it kinda worked (only for the first video in the loop of course):
<% #node.videos.each do |vid| %>
<%= image_tag("http://img.youtube.com/vi/#{vid.content}/1.jpg", :id => 'img_div') %>
<div id="vid_vid"> <%= vid.id %></div>
<% end %>
$('#img_div').on({
'click': function() {
var vid_id = document.getElementById("vid_vid").innerHTML;
$.post("/videos/iframize/", {video_id: vid_id});
}
});
Do you have rails_ujs included via jquery_ujs in assets/application.css file
= link_to image_tag("http://img.youtube.com/vi/#{vid.content}/1.jpg"), "url_path", :method => :post, :remote => true
Convert the following into a rails path: like iframeize_videos_path (generated via rake routes)
:controller => 'videos', :action => "iframize", :video_id => vid.id,
You need to differentiate the url options, html options.so use
<%= link_to image_tag("http://img.youtube.com/vi/#{vid.content}/1.jpg"),
{ :controller => 'videos', :action => "iframize", :video_id => vid.id },
{ :method => :post, :remote => true} %>

How do properly delete this?

Something is seriously not adding up here.. My page just refreshes, nothing happens, it never touches any of my debuggers hanging out on all my methods except for index.
my html:
<%- for image in #images %>
<%= image.attachment_file_name %>
<%-# link_to_delete image, :url => destroy_image_admin_wysiwyg_path(image.id) %>
<%= link_to 'delete', { :url => destroy_image_image_path(image.id) },
#:confirm => 'Are you sure?',
:post => true
%>
<br />
<% end %>
my controller
def destroy_image
debugger
#img = Image.find(params[:id])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
My routes:
map.resources :images, :member => { :destroy_image => :post }
My disgusting hack that works that I will replace as soon as I find something better
I moved the action over to a simpler controller I built myself.
Changed my routes to :
admin.resources :wysiwygs, :member => { :destroy_image => :post }
Changed my html :
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image" %>
But when I clicked on the link..it brought up.. the show action ?? fffffffffuuuuuuu
I retaliated by just moving my action to the show action, and passing a hidden field in my html..
<%= link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :hidden_field => {:value => image.id} %>
def show
# this was previously in destroy_image
#img = Image.find(params[:hidden_field][:value])
#img.destroy
respond_to do |format|
format.html { redirect_to admin_image_rotator_path }
end
end
It seems you're going down the wrong path here. If a before_filter is blocking your action, figure out why. Use skip_before_filter :filter_name if the filter is not needed.
Don't use show actions or HTTP GET for deletes. Even if it works, it will confuse things down the road. Use a DELETE verb:
map.resources :images, :member => { :destroy_image => :delete }
pass it in the link helper:
<%= link_to "delete", destroy_image_image_path(image), :method => :delete %>
And use ImagesController#destroy_image to perform the action. Better yet, consider using the standard RESTful ImagesController#destroy which map.resources gives you for free.
Not sure what was wrong in the first place, but in your second, working solution, i think you should write your link_to as follows:
link_to 'delete', :controller => "wysiwygs", :action => "destroy_image", :id => image.id
That at least would send you to the correct action.
Depending on your routes, you will have to make this a method => :post or not.
Check your rake routes output, it will show you what are the possible routes, and also what names they got, which in turn you can use as a method (add _path or _url at the end). Then it should be even easier to write something like:
link_to 'delete', wysiwygs_destroy_image_path(image)
Good luck!
You're doing a POST but your resource says that :destroy_image is only available via GET. Try changing your route to:
map.resources :images, :member => { :destroy_image => :post }
Also, take a look at your link_to. The second parameter takes a URL, not a hash that has a :url key. As mentioned elsewhere, depending on your Rails version you may need :method => :post instead of :post => true. In Rails 2.3.8, you would want this line instead:
<%= link_to 'delete', destroy_image_image_path(image), :method => :post %>

Resources