Stafford Brooke

i build things with text, eat tacos, and race myself

Rails 4: My First Run-in with Turbolinks

I’ve recently been working on a couple Rails 4 applications. I tend to be a hands-on learner, while I have been reading Andy Lindeman’s book Upgrading to Rails 4, most of my learning has come from working on applications (and begging Andy for help when I get stuck). Deprecation warnings help greatly in figuring out changes to Rail’s API, but new features such as Turbolinks can prove to be a little trickier to understand, especially when they just seem silently work.

As the README states:

Turbolinks makes following links in your web application faster. Instead of letting the browser recompile the JavaScript and CSS between each page change, it keeps the current page instance alive and replaces only the body and the title in the head. Think CGI vs persistent process.

The README goes on to claim that Turbolinks can load pages up to twice as fast. If you are building a Rails 4 application from scratch, your application will have Turbolinks enabled by default. Since it requires no configuration, you might not even notice it is there; at least not until you run into an issue with your JavaScript or CoffeScript events.

Gotcha!

My first run-in with Turbolinks was when I was trying to add click events to elements on the page for a rating feature. I was using jQuery so my CoffeeScript was similar to this:

jQuery ->
  $("span.star").on "click", ->
    ... code dealing with ratings ...

This code worked perfectly while I was developing this rating feature because I was directly loading the page. However, my rating feature was not working when I navigated to this feature from another page. This is because when I clicked on the link to thepage with the feature Turbolinks loaded the page. There was no document ready event fired, so the above code wasn’t executed; therefore my rating feature wasn’t working.

Since I hadn’t really read up on Turbolinks that this point, I didn’t immediately understand what was going on. I could refresh the page and everything would work as excepted. Chrome was showing no JavaScript errors; I was scratching my head. After a little debugging of my CoffeeScript I convinced myself that code was fine. Then I realized that the bug with my feature was probably due to Turbolinks. At this point a took a break from coding and read up on Turbolinks.

Solutions

In reading about Turbolinks, the first solution to catch my eye was to simply disable Turbolinks on the link to the page with my rating feature. At this point I had already spent a fair amount of time on my rating feature. I was ready to move on to something else so I chose the following method to get my feature working:

= link_to "My rating feature", rating_feature_path,
  "data-no-turbolink" => true

While this worked, I was kind of bummed about having to disable Tubrolinks. I took a look at Andy’s book to see if he had a better solution; and he did! I removed the data attribute on the link disabling Turbolinks. Then I refactored my CoffeeScript to to use the pattern Andy recommend in his book. Now my code looks something like this:

attachRatingHandler = ->
  $("span.star").on "click", ->
    ... code dealing with ratings ...

$(document).ready attachRatingHandler
$(document).on "page:load", attachRatingHandler

Now the code works with Turbolinks. The “page:load” event will be triggered with Turbolinks loads a page. The “ready” is still needed for when the full page is loaded.

UPDATE: A Better Solution

After I published this article Simon Courtois pointed out that we could simplify the code above using JQuery’s on method. Using this method to simplify our code we end up with something like:

attachRatingHandler = ->
  ... code dealing with ratings ...
$ ->
  $(document).on 'click', 'span.star', attachRatingHandler

Thanks Simon!

Lessons Learned

Aside from learning about how Turbolinks works, I also learned that I should probably spend a little more time reading about the changes to Rails in Rails 4. I could have probably saved a fair amount of time if I had understood how Turbolinks worked up front.