Forward Slash.

a codetrip to the wild west

An Ajax Call by Example

Last Wednesday, I spent all day setting up an ajax call for a portion of the project my Flatiron team is currently working on (check it out here!). It was a difficult process, piecing all the different elements together, but I did learn quite a bit. So today, I’m going to show you how to make an ajax call in a rails app using our example.

A quick summary of the problem: one feature of our project is a show page for all the different politicians currently in office in the US senate and house. We wanted to pull in recent New York Times articles to display on their show page, however because our app had to make multiple api requests to the NYTimes server (up to 50) and do some more filtering on our end after we received the data, each politician’s show page was taking an excruciatingly long time to load (up to 20 seconds). Rather than have the user wait that long before a page loads, we wanted the page to load immediately, then make an ajax call to fetch the articles, persist them to our database, then display them on the page.

Let’s back up for a bit, what exactly is ajax? It’s a tool that allows you to make requests to a web server without the need to refresh your page. It’s how things like infinite scrolling work. Once you reach a certain point in the page, an ajax call is triggered, it makes a request to get more data and load it on the page. You can see how this might come in handy.

Back to our problem, we have a method defined in our model called get_articles which made the api request and handled any post processing on our end, it was this method that took too long to run. Originally, we were calling it in the show action of our politician controller. Line 4 below:

1
2
3
4
5
6
7
8
def show
  @politician = Politician.find(params[:id])
  if @politician.nytimes_articles.empty?
    @articles = @politician.get_articles
  else
    @articles = @politician.nytimes_articles
  end
end

Now because this method took anywhere between 5 and 20 seconds to run, once you clicked the link to go to a show page, you would have to wait that long before the page loaded. Because no one wants to wait that long for a page to load, let’s look at what we had to do to set up the NYTimes ajax call.

First, let’s separate the api request from the show action, and make a separate action in the same controller called times_articles to move that code into, so now our controller looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
def show
  @politician = Politician.find(params[:id])
end

def times_articles
  @politician = Politician.find(params[:politician_id])
  if @politician.nytimes_articles.empty?
    @articles = @politician.get_articles
  else
    @articles = @politician.nytimes_articles
  end
end

Then we need to add a nested route in our routes.rb file, one which will run the new action we just created:

1
2
3
resources :politicians, only: [:show] do
  get "/times_articles" => 'politicians#times_articles'
end

So for each polititian, this creates a route called times_articles: /politicians/:politician_id/times_articles. When the ajax call hits this route, it will run the times_articles controller action

Next we need some place to actually house the ajax call. So we create a .js file in app/assets/javascript, in our case, we called it politician.js to stick with our naming convention since that is what our model/controller/migration was called. If you haven’t required tree in your application.js file, then you’ll need to make sure to require the new file in there. This javascript code will load on every page, but if you look at the way it was set up (line 2 below), the code only executes on politician show pages.

1
2
3
4
5
6
7
8
9
10
11
12
$(document).on("page:change", function(){
   if($('body').is('#politicians.show')){
    var politician_id = $(".politician_id").attr('id')

    $.ajax({
      url: '/politicians/' + politician_id + '/times_articles',
      dataType: "script",
      type: "GET"
    });

    }
})

This code is making an ajax call to the url we’ve specified, which will hit our new controller action. You also have to specify the type of request, in this case a get request, and the type of data you are expecting back, here “script” is telling the ajax call to be expecting javascript back, after which it will execute the javascript. But before this code can sucessfully execute, there are are few more things we need to set up.

Since our ajax call is requesting javascript, we need to add a few lines of code into our times_articles action that will specify what to do when someone requests javascript.

1
2
3
4
5
6
7
8
9
10
11
12
13
def times_articles
  @politician = Politician.find(params[:politician_id])
  if @politician.nytimes_articles.empty?
    @articles = @politician.get_articles
  else
    @articles = @politician.nytimes_articles
  end

  respond_to do |format|
    format.html {render action: "show"}
    format.js
  end
end

Basically, the respond_to block is saying if someone requests javascript (in this case the “someone” is our ajax call), do the default action (because of rails magic, the default action is to look for a .js.erb file in the views directory with the same name as the action: times_articles.js.erb). If for some reason html is requested, for example if someone typed the times_article path into their browser, it would just load the show page.

So let’s create that file which our controller is looking for: times_articles.js.erb, our specific file contained the following code:

1
2
3
4
5
$('#articles img').hide();

<% @articles.each do |article| %>
$('#articles').append("<div id='indiv-article' class='col-xs-4'> <%= j link_to article.headline, article.url, class: @politician.party %> </div>");
<% end %>

What it is doing is hiding an img tag (in our case a loading animation) and adding links for each article to the DOM in the section that has an id of ‘articles’. This code is the “script” which the ajax call is expecting back, which it will then execute.

Let’s summarize, what we have set up does the following:

  1. The javascript in politicians.js runs when we are on a politician show page and the ajax call is made.
  2. The ajax call hits the times_articles url, making a get request for javascript, which then runs the respective controller action
  3. The controller action runs the method to make the api requests to the NYTimes server, persists that data to the database, then looks for a file called times_articles.js.erb to return to the ajax call
  4. The code in times_article.js.erb is returned to the ajax call and executed, hiding certain html elements and adding new ones into the DOM, reflecting in your browser without the page reloading.

Snazzy, eh?