Rails remote delete and update view through Ajax - ajax

In the past, whenever I wanted to update a part of my view through Ajax, I've done the following:
create a partial out of the part I want to update and give it a unique ID, say #tracks
create a special action in the controller for that Ajax call, say remove_track that updates all the values, etc. and add format.js
create a new JS file with the same name as the action so Rails calls it automatically remove_track.js.erb which contains something like: $('#tracks').html("<%=j render 'cds/show_tracks' %>");
set remote: true in the link that calls this action.
All this is fine, but now I am trying to delete and update a common index view using the regular destroy method for flexibility, meaning I can call this method either through Ajax or normally. I figured it's such a common thing to do that there has to be a better way than all of the above.
I can get the destroy method to call my destroy.js.erb file by simply putting this into the controller:
format.js { layout: false }
and of course setting remote: true on the link.
what I cannot do is get the view to refresh. The table I want to refresh is encased in a div with a unique ID, but since it's not a partial, it refuses to refresh the content. Maybe I'm missing something.
Am I doomed to have to create a partial and refresh it with the method above or is there a more magical way of doing it (other than using Turbolinks)?
Thanks.
PS
Also, I just noticed this has the added disadvantage that I cannot pass the rest of the params to the destroy method since it only passes the object ID to destroy using the regular CRUD routes. If I try to use platform(action: destroy) or platform(method: delete) I get an error:
No route matches {:action=>"destroy", :controller=>"platforms"}
Which means I have to create a new route if I want to pass those parameters...
Yet another disadvantage to all this is that I'm repeading all the logic for searches and sorting that I have in the index method again in the destroy method. I am certain this is definitely not the way to do it.

Thanks to this page I found the proper way to do it. So simple and effective.
http://carmennorahgraydean.blogspot.com.es/2012/10/rails-328-ajax-super-basic-example.html
Update your destroy line in index.html.erb:
<%= link_to 'Destroy', pony, method: :delete, data: { confirm:
'Are you sure?' }, :remote => true, :class => 'delete_pony' %>
Create a file, destroy.js.erb, put it next to your other .erb files
(under app/views/ponies). It should look like this:
$('.delete_pony').bind('ajax:success', function() {
$(this).closest('tr').fadeOut();
});
Add format.js { render :layout => false } to your controller:
respond_to do |format|
format.html { redirect_to ponies_url }
format.json { head :no_content }
format.js { render :layout => false }
end
Hope this helps someone else.

Related

Ruby render partial on form submit on same page

I am trying to render a partial that I made on top of the current page after a form submit. This should happen in the controller's create method, but I can't seem to figure out if this can be done in Ruby, or if I should just render it using Javascript.
Here is what I have tried so far:
Created a partial in the same folder as the new.html.erb (called _lightbox.html.erb)
The controller render changed to this:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
BackgroundJob.perform_async(#user.id)
format.html { render :partial=>"lightbox", :layout=>false}
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
I have read up on render, however using the :partial key it simply renders the partial on a new page. I should probably just change the form to use :remote and add the lightbox-esque content with an ajax success, however I was curious as to if this can be done with Ruby on Rails with its rendering functionality.
The code you've displayed here isn't really the problem. It's all in the view.
You need to prevent the default action from form submission, which is to post to the form URL. If the browser posts to that URL, it will render the response. Once you've prevented the default action, you'll then need to submit the form yourself using javascript and set up a callback to deal with the response. That callback should put the html in the place you want it in the page. If any parts of this are unclear, I can expand upon them.

How to setup a rails 3 route for a nested resource and custom controller action

I have a recommendation that is nested below Categories and Awards.
So Category/:id/awards/:id/recommendations/:id
I have an Assets model that handles paperclip attachments to the Recommendation. A Recommendation has_many Assets, Assets belong_to :recommendation
In my Recommendation new/edit views I am rendering a form partial (as is normal) that gives the user the option to upload several assets.
If there are Assets already related, then it lists them. I am working on setting up a custom delete action on the recommendations controller.
My current link_to:
<%= link_to "Delete Attachment",
{:controller => :recommendations, :action => :destroy_asset, :id => asset.id },
{:remote => "true", :confirm => "Are you sure you want to delete this image?"}
%>
My Controller action:
def destroy_asset
##recommendation = Recommendation.find(params[:id])
#asset = Asset.find(params[:id])
#asset.destroy
respond_to do |format|
format.js
end
end
routes:
resources :recommendations
resources :categories do
resources :awards do
resources :recommendations
end
end
I am still learning remote => true, and how to route this sucker. Not sure if I need to have the route nested or not. I tend to think not. Since I have an Asset.id in my loop, I should just be able to execute the destroy without needing the Recommendation at all.
So the question/s: do I need a route to access a custom action in my recommendations_controller?
thanks
Try this:
resources :categories do
resources :awards do
resources :recommendations
member do
get :destroy_asset
end
end
end
end
Or If you only want only destroy_asset action under recommendation controller, do something like this :
resources :recommendations
member do
get :destroy_asset
end
end
Or If you want custom match, do something like this :
match "asset/:id/destroy_asset", :to => "recommendations#destroy_asset",
:as=> "destroy_asset"

Updating (Hiding) a group of objects in Rails 3

I've some difficulty in determining any way to update my objects as a group. I have an Alert model which has some alerts for the user. I'd like to provide a way to update them in bulk, as I've noticed that I often want to. I just don't know Rails well enough to know what 'the Rails way' would be.
My controller:
class AlertsController < ApplicationController
def index
#alerts = Alert.all(show: true)
end
def destroy
alert = Alert.get(params[:id])
## I lied, I'm not really deleting things, just hiding them from showing
alert.update(show: false) if alert
respond_to do |format|
format.js { render :nothing => true }
end
end
I've been using this code in my *.html.haml view to create the delete link for each individual alert:
- #alerts.each do |alert|
%span{ :class => 'description' }=alert.description
= link_to "Destroy", alert, :confirm => "Are you sure", :method => :delete, :remote => :true
What I want to have is a link on my index page Delete All which I could use to hide(update) all of the alerts. I've been attempting this with a destroy_all method for my controller, but I felt like it wasn't quite the right approach, as I didn't know how to link to something like that. Thank you for pointing me in the right direction.
Here is the destroy_all method I'd hacked together, for reference.
def destroy_all
#alerts.each do |alert|
alert.update(show: false) if alert
respond_to do |format|
format.js {render :nothing => true}
end
end
end
I don't know how you have routed to this controller but I would probably do something like this:
#routes.rb
resources :alerts do
delete :remove_all, :on => :collection
end
This will create a route looking like /alerts/remove_all which can only be called through the DELETE protocol. By specifying :collection it tells the routing the this route is not a sub route to one specific Alert so no :id is included.
Then the controller action could probably look something like this:
def remove_all
Alert.update_all({ show: false }, { show: true })
respond_to do |format|
format.js {render :nothing => true}
end
end
The update_all function will make sure that there is only one database update instead of one for each alert that is to be removed. The first argument is a hash containing the updates and the second argument is a hash with the conditions for which records are to be updated.
And from the view you should be able to link to the action like this:
= link_to "Remove All", remove_all_alerts_path, :confirm => "Are you sure", :method => :delete, :remote => :true
remove_all_alerts_path is a path helper that is automatically generated when you use the above syntax in routes.rb

Problem rendering an Ajax partial with link_to specifying a controller action

I have a div (id="content") on a "proposition" show page into which I want to render different partials depending on which tab at the top of the div is selected.
I'm trying to render a partial (substantive_content) with AJAX using the following link_to:
<%= link_to "Connected", :url => {:controller => "propositions",
:action => "substantive_content"}, :remote => true %>
I've got two problems:
On clicking the link, a GET request
is sent to
PropositionsController#show, and
not (I presume) to the "substantive
content" action. This just refreshes the page and doesn't do anything remotely. The correct partial isn't displayed.
I'm not sure how then to render a
partial into the "content" div.
At the moment I have this in the PropositionsController:
def substantive_content
respond_to do |format|
format.js{ render :update do |rightbar|
rightbar.replace_html 'content', 'Hello World!'
end}
end
end
... but am not sure where to go from here. Am I even going about this in the right way? I'm very new to rails, javascript, etc.
Any help with either of these problems would be fantastic. Thanks!
In your routes.rb file you should have an entry for the substantive_content action. It might look something like this:
match "substantive_content" => "propositions#substantive_content", :as => substantive_content
Then in your view the link should look something like this:
<%= link_to "Connected", substantive_content_path, :remote => true %>
I would render a js.erb file when the js request comes to the substantive_content action. The action would look soemthing like this:
def substantive_content
respond_to do |format|
format.js
end
end
Then create a substantive_content.js.erb file in your views/propositions directory. In that file you can use jquery to update the content div:
$('#content').html("<%= render :partial => 'your_partial_name_here' %>");
Without knowing more about the actual application you're working on, its hard to say if this is the best way to accomplish what you want. I generally try to stay as "RESTful" as possible with my apps so I'm reluctant to create actions aren't part of the normal index, new, create, show, etc. set. That's not to say that it isn't neccesary in this case, but it's something to think about.

Using AJAX in Rails: How do I change a button as soon as it's clicked?

Hey! I'm teaching myself Ruby, and have been stuck on this for a couple days:
I'm currently using MooTools-1.3-compat and Rails 3.
I'd like to replace one button (called "Follow") with another (called "Unfollow") as soon as someone clicks on it. I'm using :remote => true and have a file ending in .js.erb that's being called...I just need help figuring out what goes in this .js file
The "Follow" button is in a div with id="follow_form", but there are many buttons on the page, and they all have an id = "follow_form"...i.e. $("follow_form").set(...) replaces the first element and that's not correct. I need help replacing the button that made the call.
I looked at this tutorial, but the line below doesn't work for me. Could it be because I'm using MooTools instead of Prototype?
$("follow_form").update("<%= escape_javascript(render('users/unfollow')) %>")
ps. This is what I have so far, and this works:
in app/views/shared:
<%= form_for current_user.subscriptions.build(:event => #event), :remote => true do |f| %>
<div><%= f.hidden_field :event %></div>
<div class="actions"><%= f.submit "Follow" %></div>
<% end %>
in app/views/events/create.js.erb
alert("follow!"); //Temporary...this is what I'm trying to replace
*in app/controllers/subscriptions_controller.rb*
def create
#subscription = current_user.subscriptions.build(params[:subscription])
#subscription.save
respond_to do |format|
format.html { redirect_to(..) }
format.js {render :layout}
end
Any help would be greatly, greatly appreciated!
The ID selector should be used if there is only one of those elements on the page, as the Id selector us for unique IDs. A Class selector is what your looking for. Classes are used for multiple elements that have the same styles or same javascript events.
The code to replace Follow with Unfollow would be as follows in Mootools. I am not totally understanding your question, but you will need to assign a ID to the button.
document.addEvent('domready',function(){
$$('follow_form').addEvent('click',function(e){
//Ajax Here
this.set('value','Unfollow');
});
});
Mootools has a different set of functions than Prototype, so most (if not all) code designed for prototype will not work in Mootools. If you want to see the functions in Mootools, I recommend the Docs (its a good read): http://mootools.net/docs/
This code would work as well for you, if you do not/can not add unique IDs to each form element:
$$('input').addEvent('click',function(){
if(this.value == 'Follow') {
//Ajax Here
this.set('value','Unfollow');
}
});

Resources