jquery-ujs: Event ajax:complete does not get triggered after form-replacement

Hey.

Today I upgraded from jquery-rails gem 1.0.16 to 1.0.17. I found out that the “ajax:complete” event is not always triggered anymore.

I prepared a simple Rails app to show the problem: https://github.com/easychris/jquery17-rails-ujs-broken

In application.js I want to trigger all ajax:complete events for existing and new forms (hence the .live call):

$(function() {
  $('form').live('ajax:complete', function() {
    alert('ajax:complete event triggered');
  });
});

I prepared two sample data-remote=true forms, one page using jQuery 1.6.4, the other 1.7. The ajax call simply returns javascript code to replace the existing form by another form (in real life the new form would contain error messages etc).

$('#test_form_container').html(' <%= j (render :partial => 'form')  %> ');

When using jQuery 1.6.4 the ajax:complete event gets triggered, but unfortunately not using 1.7. I found out that in jquery-ujs the line

element.trigger('ajax:complete', [xhr, status]);

gets called, but it doesn’t trigger any event. I guess because the form to which the ajax event belongs doesn’t exist anymore (as it got replaced by the new form). But it should work, as I use the .live event to bind the event to any form-tag. And it does work in 1.6.4.

Any ideas? I tried a couple of hours to find the error, without success. Maybe someone else already has an idea?

About this issue

  • Original URL
  • State: closed
  • Created 13 years ago
  • Reactions: 2
  • Comments: 36 (13 by maintainers)

Most upvoted comments

@stayhero Why is this issue closed ? I’m on Rails 5.1 and still having this problem: ajax:success is never called if the original element is removed by the reponse.js.erb file.

I do this in my EJS to solve the problem:

setTimeout(function () {
  $('#test_form_container').html(' <%= j (render :partial => 'form')  %> ');
});

In this way, the html replacement will be delayed to the next time tick that happens after the ajax:* events.

@shnikola 's solution seems to work smoothly without touching any js.erb files:

$(document).on('ajax:send', function(event, jqXHR){
  jqXHR.always(function(){
    // ...
  });
});

@lacco Thank you for your answer 👍

When we use Rails 5.1 or later, add this gem:

gem 'jquery-rails'

then change application.coffee

#= require rails-ujs

to

#= require jquery_ujs

now, everything will go well!

# application.coffee
$(document).on "ajax:send", (event, jqXHR) ->
  # For ajax:send

  jqXHR.always ->
    # For ajax:complete
  jqXHR.done ->
    # For ajax:success
  jqXHR.fail ->
    # For ajax:erro

@tomrossi7 The best solution is to not use a js.erb response to replace the element that triggered the ajax request, because if you do, then the ajax:complete event (which fires after jquery evaluates the response javascript) has no element left in the page DOM on which to fire. If you want to replace the element that triggers an ajax request after the request is made, then your best bet is to respond with the HTML you want replaced and the replace it using jQuery in either the ajax:success or ajax:complete callback so that you have control over the order of events.

Curious, has anyone found a way to trigger an event after a replacement without resorting to the global ajaxComplete?

I have solved this in application.js with the following:

$('form').on('ajax:sendBefore', function(event, jqXHR) {
  jqXHR.always(onAjaxComplete);
});

That way, you don’t have to change all your EJS files.