user101289
user101289

Reputation: 10422

rails 4 ajax event not triggering success

I'm working on my rails 4 demo app, and I've got a button that is meant to do some work on the server side, then return json with a success or failure message.

Similar to this question, I am seeing the response come back with a 200 code and the expected json payload, but the jQuery success event is not triggered-- to make it more confusing, it seems to be somewhat intermittent-- sometimes I get the proper action (showing a flash message and removing a div) but most of the time nothing happens.

Here's the button in the view:

<%= link_to 'Do Work', widget_path(:id => widget.id), id: widget.id, remote: true, method: :get %>

Here's the controller:

respond_to do |format|
  if widget.save
    // this json is returned properly
    format.json { render json: { status: 'success', msg: 'Widget created.', :content_type => 'text/json', type: 'info', item: 'widget' } }
  else
    format.json { render json: @user.errors, :content_type => 'text/json', status: :unprocessable_entity }
  end
end

The javascript in application.js:

var fade_flash = function() {
    $("#flash_success").delay(2000).fadeOut("slow");
    $("#flash_info").delay(2000).fadeOut("slow");
    $("#flash_warning").delay(2000).fadeOut("slow");
    $("#flash_danger").delay(2000).fadeOut("slow");
};
fade_flash();

var show_ajax_message = function(msg, type) {
    $("#notice").html('<div id="flash_'+type+'" class="alert alert-dismissable alert-'+type+'"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>'+msg+'</div>');
    fade_flash();
};

Here's the coffeescript in widget.js.coffee:

$ ->
  $("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
    alert('got here')
    console.dir(data)
    if data.status == 'success' 
        show_ajax_message(data.msg, data.type) 
        if data.item == 'widget'
            $('#widget_'+this.id).hide('slow')

Upvotes: 1

Views: 864

Answers (1)

Richard Peck
Richard Peck

Reputation: 76774

Turbolinks

Your problem is likely to be an issue with Turbolinks:

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

Turbolinks basically loads your pages with Ajax (leaving <head> tags in place), loading your pages faster... but also causing your JS to not fire

Issue being JS works by binding to DOM elements at load. If DOM elements are not present, they can't be bound, causing problem


Fix

The way you need to fix this is to use javascript delegation

This works by binding events to a "container" element (typically document) and delegating down to the sub elements. This works because document is always present, and consequently JS will always be able to fire whether the DOM elements are present at load or not:

#app/assets/javascripts/application.js
var fade_flash = function() {
    $("#flash_success").delay(2000).fadeOut("slow");
    $("#flash_info").delay(2000).fadeOut("slow");
    $("#flash_warning").delay(2000).fadeOut("slow");
    $("#flash_danger").delay(2000).fadeOut("slow");
};

var show_ajax_message = function(msg, type) {
    $("#notice").html('<div id="flash_'+type+'" class="alert alert-dismissable alert-'+type+'"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>'+msg+'</div>');
    fade_flash();
};

$(document).on("read page:load", fade_flash);

#app/assets/javascripts/widgets.js.coffee
$ ->
  $(document).on "ajax:success", "a[data-remote]", (e, data, status, xhr) ->
    // meaningless comment to get past edit limit-- added comma in line above
    alert('got here')
    console.dir(data)
    if data.status == 'success' 
        show_ajax_message(data.msg, data.type) 
        if data.item == 'widget'
            $('#widget_'+this.id).hide('slow')

Upvotes: 1

Related Questions