Reputation: 10422
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">×</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
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">×</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