Undocumented UJS changes when upgrading to Rails 5.1

inopinatus

I was pleased to see Rails 5.1 drop jQuery as a dependency. As a result the Unobtrusive JavaScript (UJS) driver has been rewritten to operate without jQuery. For most remote forms and links, this drop-in replacement might just work straight away. However, for those hooking into UJS more tightly, there are some gotchas. These aren’t difficult changes but they’re undocumented, and since UJS no longer comes as a separate gem (it is now built into Action View) they’re slightly harder to investigate.

I went through a 5.1 upgrade recently, and here are some notes that you won’t find in the documentation.

New handleRemote call signature

Sometimes you want to invoke the UJS driver directly. I have code that is programatically submitting a remote form, and for the sake of consistency with Rails’s regular form pre-processing, it is invoking UJS’s handleRemote to do so.

However the new UJS driver’s handleRemote has four changes.

  • Firstly, it makes an assumption about this being the intended element, which in the old jquery-ujs we’d specify via an argument. So we need to assign this using call().
  • Secondly, unsurprisingly, it wants a DOM element not a jQuery object.
  • Thirdly, it wants an event argument so that it can halt event processing on the target.
  • Finally, it is no longer hanging off the jQuery global but has moved to the new Rails application variable.

Therefore, where I might previously have written:

$.rails.handleRemote($('form#new_user'))

I now write:

Rails.handleRemote.call(document.querySelector('form#new_user'), event)

Event handlers

Perhaps since jQuery-specific idioms are no longer available, the signature of calls to UJS’s event handlers has changed. Where previously I had:

function handleAjaxSuccess(event, data, status, xhr) {
  // ...
}
$(document.body).on('ajax:success', handleAjaxSuccess)

this became:

handleAjaxSuccess = function(event) {
  ref = event.detail, data = ref[0], status = ref[1], xhr = ref[2];
  // ...
}
document.body.addEventListener('ajax:success', handleAjaxSuccess, false)

In CoffeeScript that destructuring becomes prettier, [data, status, xhr] = event.detail.

Also note, the event will now be a CustomEvent rather than a jQuery Event.

UJS source location & distribution

The UJS driver used to live in the jquery-rails gem. It is now distributed as part of Action View. I am a puts debuggerer, and this makes debugging slightly harder, because the actionview gem compiles UJS from CoffeeScript before distribution. Mercifully, however, it is not minified, so you can still grok the code in development. If you want the source, it’s here.

For a deeper dive into the new UJS code, Chris Oliver’s GoRails has a handy guided tour (subscription required).