Simple Ajax calls in Rails
Let's suppose you have a basic menu like this one
And you want the Premium state (any boolean attribute) to change on click for a given local (any object).
A really neat way for doing these kind of things is through AJAX Calls, that is changing the state of the DOM tree without reloading the page.
Setting everything up
What I do is to write some specific routes for this, and I only want those routes to be accessible though AJAX and AJAX only. The following does the trick
#config/routes.rb
Rails.application.routes.draw do
class OnlyAjaxRequest
def matches?(request)
request.xhr?
end
end
#rest of the routes
end
We'll come to this later.
Now see the view
#app/views/show.html.erb
...
<a href="" data-remote=true id="unique_id" class="premium-state"><%= local.premium %></a>
...
and notice the data-remote field, this is fundamental for what we want. It says to the controller that when we click this link, we don't want it to be sent to the regular HTML format procedure. Rather we want the JS one.
In the controller (mind this is just a quick way to write the controller method, but you should check for example, if the update_attributes
is successful
#app/controllers/locals.rb
def set_premium
local = Local.find params[:local_id]
if local.premium #if local is premium, we set it to false
local.udpdate_attributes(:premium => false)
@message = "true"
else
#same thing for premium false
end
respond_to do |format|
format.js
end
end
See the format.js line there? It saying the controller: 'don't render the html response (the view), instead render the javascript response.
Now, when you respond with HTML you need to have a view in html format. Now you need one with js.erb
extension.
#set_premium.js.erb
$("#unique_id").text("<%= j @message %>");
This code will be executed when you click the unique_id
link in the view.
But wait...
We haven't even set the route for this action.
#config/routes.rb
resources :locals do
post 'set_premium' => 'locals#set_premium', constraint: OnlyAjaxRequest.new
There it is. The constraint
part indicates that this call has to be made through AJAX.
How do I call this route
Did you notice the <a>
element did not have an href field? I do that through Javascript
#app/assets/javascripts/locals.js.coffee
$(".premium-state").on "click", ->
local_id = $(this).attr('id');
$.ajax {
url: ROUTE_TO_YOUR_ACTION
type: 'post'
dataType: 'script'
success: () ->
}
where ROUTE_TO_YOUR_ACTION
is the path that the rake routes
command pukes for that specific controller action.
A couple of remarks:
- the dataType field of the AJAX call is 'script'. Think of that as the AJAX requesting a script file and executing it.
- the
sucess
function does not have instructions. The instructions go in the Javascript view for the method you requested! - the
type
field has to be consisting with the naming of the custom route.
aaaaaaand we're done.
And I think that's it. Your AJAX button should be working by now.
Feel free to point out any inconsistency in the article, so I can fix it/explain it.
Written by Sebastián González
Related protips
3 Responses
hello bro. I'm getting error. it says
500 (Internal Server Error)
Hey bro, you should not define the class "OnlyAjaxRequest" within the routes block... Defining a class withing a block is a bad practice.
I also think you missed the 'data' param in the ajax call so the controller can have access to the parameter :local_id