Pragmatic Pineapple 🍍

render_async 2.1.6 released

Published Last updated loading views

In this post, we will go through all the changes that are in the new 2.1.6 version.


Photo by Nick Karvounis on Unsplash

Some of the new changes and bug fixes were pretty tricky to solve. I wanted to write a blog post to have detailed hows and whys of how I fixed them.

The new version mainly brought bug fixes:

Let us dive into details of how each was fixed:

If you have not heard about Turbolinks before, it is an excellent gimmick for your Rails application that makes it behave like a single-page application. It gracefully avoids full page loads when you click a link, for example. Turbolinks does this by fetching a page, swapping in its <body> tag, and merging its <head> with existing content on the page. The gem has been well adopted in the Rails world, and when you do rails new my-awesome-app, it comes equipped with Turbolinks gem from the start.

Before 2.1.6 version, if you used render_async’s polling feature, render_async did not handle navigation changes with Turbolinks. For example, if your page started polling and you clicked a link, it wouldn’t stop polling. The issue with polling sucked big time! Fortunately, in the new version, this is no more!

There is no more polling issue because we now call clearInterval if the turbolinks:visit event happens:

$(document).one("turbolinks:visit", function () {
  if (typeof _interval === "number") {
    _interval = undefined

Default header X-Requested-With is now set

If you are using Vanilla JavaScript part of the gem (you don’t have jQuery in your project, or you want Vanilla JS for some reason), each request was missing X-Requested-With header. This header is important and is set by jQuery by default. It is set by default because some servers check this header as a precaution from CSRF attacks. More about CSRF attack and X-Requested-With header can be found in this great blog post.

Fix was this was pretty easy and straightforward:

request.setRequestHeader("X-Requested-With", "XMLHttpRequest")

Event delegation now works when toggling

If you wanted to trigger the loading of render_async, you could use any element to do so. For example, you wanted to load comments on your blog with render_async you would so something like this:

<!-- app/views/posts/show.html.erb -->

<%= render_async comments_path, toggle: { selector: '#comments-button',
                                          event: :click } do %>
  <a href='#' id='comments-button'>Load comments</a>
<% end %>

The method above worked fine if you did not have any other events depending on that link click. But issue occurred to one user when he tried toggle feature on tabs in his UI. He wanted to load content when the user was changing tabs in his app. Line event.preventDefault() inside toggle logic was stopping the changing of tabs. Events did not delegate to the tab-changing logic, and the tab was never changed. Fortunately, in the new 2.1.6 version, this is not happening anymore!

Problem with nested partials was the last moment discovery by ye-ling-aung in his comment. Nested partials did not load with Turbolinks. If, for some reason, you needed to nest render_async calls into one another, you could do it quickly. The problem occurred when the page loaded with Turbolinks. The top-level render_async call got rendered, but the nested calls below did not. It was because render_async loading logic is triggered when turbolinks:load event gets triggered. Triggering loading on turbolinks:load is fine for the top-level (initial) render_async call. But, if you have a nested call, it will wait for turbolinks:load, which will not happen, because the page already loaded.

In order to have nested calls render, I added a piece of logic which checks whether the document state is either ‘complete’ or ‘interactive’:

if (
  document.readyState === "complete" ||
  document.readyState === "interactive"
) {
  // Call render_async rendering logic

This way, when initial render_async calls loads and renders nested calls, they are sure to be performed.

Final thoughts

Fixing and shipping these fixes was such a relief! I am thankful and glad for everyone that kept using render_async that were having these issues. I also hope that more people will try out the new version and report if they have any problems!

P.S. If you like my work on this gem so far, and you want me to keep improving and maintaining it, consider sponsoring me on GitHub Sponsors or through PayPal.

Nikola Đuza

Written by Nikola Đuza who helps developers improve their productivity by sharing pragmatic advice & applicable knowledge on JavaScript and Ruby. You can connect with him on Twitter.

© 2024 Nikola Đuza