Last Updated: September 29, 2021
·
14.25K
· sebastialonso

Simple Ajax calls in Rails

Let's suppose you have a basic menu like this one

Ugly view

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.

3 Responses
Add your response

hello bro. I'm getting error. it says

500 (Internal Server Error)

over 1 year ago ·

Hey bro, you should not define the class "OnlyAjaxRequest" within the routes block... Defining a class withing a block is a bad practice.

over 1 year ago ·

I also think you missed the 'data' param in the ajax call so the controller can have access to the parameter :local_id

over 1 year ago ·