New ajax:aborted Rails jQuery UJS Hooks

In my last article, Rails jQuery UJS: Now Interactive, I talked about my push to make Rails jQuery UJS (aka jquery-ujs) more interactive and easier to customize and extend with third-party plugins. Along those same goals, let's discuss another recent change: ajax:aborted hooks.

There are now two scenarios where remote forms will not be submitted, and the AJAX submission will instead be aborted. When this happens, jquery-ujs now publishes hooks, to which we can bind handler functions to gracefully handle these situations.

Let's take a look at each of these scenarios and how we can utilize the event hooks.

Scenario Hook name blank required fields ajax:aborted:required non-blank file inputs ajax:aborted:file

Required fields

The Opera browser has some interesting behavior (useful, but inconsistent) in which forms do not get submitted if they contain any blank required inputs. So, submitting a form containing the following blank fields would cause nothing to happen:

... <%= f.text_field :name, :required => true %> <%= f.text_area :bio, :required => true %> # ...which produces: <input id="person_name" name="person_name" required="required" /> <textarea id="person_bio" name="person_bio" required="required" /> ...

So, we implemented the same functionality in jquery-ujs for all browsers. If there are any blank required fields in a remote form, the AJAX submission is aborted, and we trigger the event hook `ajax:aborted:required`. A simple example for using this hook could look like this (in our application.js or whatever):

$('form').live('ajax:aborted:required', function(event, elements){ elements.each(function(){ $(this).addClass('blank-required'); }); });

Notice that the required fields that caused this event to trigger are passed into our handler function, allowing us to do whatever we want with them.

Plugging in

Also, we may want to override the default behavior of jquery-ujs, and submit the form via AJAX anyway. We can do this by binding to the `ajax:aborted:required` hook with a handler function that returns false. For example:

$('form').live('ajax:aborted:required', function(event, elements){ // If user answers 'OK' to confirm dialog, we return false return ! confirm('Submit with missing information?'); });

Keep in mind that Opera does not trigger the submit event for forms with missing required fields, so the `ajax:aborted:required` event would never get fired in Opera, and thus, neither would any handlers bound to this hook.

File upload fields

Due to security restrictions, browsers do not allow file uploads via AJAX. There are workarounds, including the iFrame method, which allow us to simulate AJAX file uploads, but they tend to be a little more involved. So, jquery-ujs now aborts attempted AJAX file uploads, and instead makes it really easy for third-party plugins to extend rails.js to handle this functionality.

The following would cause jquery-ujs to abort the AJAX submission and fire the `ajax:aborted:file` event hook:

... <%= f.file_field :avatar %> # ...which produces: <input type="file" id="person_avatar" name="person_avatar" /> ...

For the curious, if we try to submit a form containing a file input field via AJAX with jQuery, an AJAX request will be submitted to the server, but with the file input stripped out from the request parameters.

Unlike the required field functionality of jquery-ujs, file uploads don't cause the form submission to be completely aborted. Instead, only the AJAX form submission, allowing the browser to submit the form normally with a page refresh. Also note that this behavior only occurs for non-blank file inputs; otherwise, the form is AJAX-submitted as usual.

Plugging in

But what if we want to cancel the browser's standard form submission and provide our own AJAX file upload functionality, like the iFrame method mentioned above? That's where the `ajax:aborted:file` hook comes in handy. We can bind a handler to this event hook that returns false to prevent the browser's default functionality:

$('form').live('ajax:aborted:file', function(event, elements){ // Implement own remote file-transfer handler here for non-blank file inputs return false; });

Third-party gems and plugins, like our own Rails Remotipart gem, can also use this event to automatically intervene for file uploads and provide AJAX upload functionality.

Yeah buddy

I'd like to send a shout-out to Neeraj Singh and Mislav Marohnić for helping get these changes pulled into Rails jQuery UJS.

In other news, I'm now on the Rails jQuery UJS core team. So, stay tuned for more on the latest developments in jquery-ujs. :-)

If you liked this, check out our Definitive Guide to Rails 3 Remote Links and Forms Part 1 and Part 2.

About the author: Steve Schwartz // Owner of Alfa Jango, CTO of Genomenon, co-founder of Carcode (acquired by Edmunds.com in 2014), engineer, developer, open-source enthusiast, guitarist, and racecar driverist.





