Reputation: 9457
For a few weeks I have been avoiding the Rails UJS "Ajax helpers" provided by rails.js and remote: true
. As a result, I was doing something like this:
// erb
<li>
<button type="button" class="btn btn-default btn-md">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span><%= tagging.tag.name %>
<%= hidden_field_tag :id, tagging.id %>
</button>
</li>
// js
remove_tag: function(){
$view.on('click','.cloud .glyphicon-remove', function(){
var id = $(this).next().val();
$.ajax({
type: "POST",
data: {id: id},
url: 'taggings/destroy',
beforeSend: function() {
$('.loading').show();
},
complete: function(){
$('.loading').hide();
},
success: function(resp){
alert( resp );
}
});
})
},
But it was obviously a lot of work to do this over and over. So I decided using remote: true
and rails.js built-in unobtrusive features was more elegant:
<li>
<button type="button" class="btn btn-default btn-md">
<%= link_to tagging, remote: true, method: :delete, class: 'remove-tag' do %>
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
<% end %>
<%= tagging.tag.name %>
</button>
</li>
Which translates into this nice code:
<li>
<button type="button" class="btn btn-default btn-md">
<a class= "remove-tag" data-method="delete" data-remote="true" href="/taggings/1" rel="nofollow">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</a>
orange
</button>
</li>
Now I can invoke the beforeSend and complete but the success ajax event is not triggered. I followed the suggestion of this link. However, the 'ajax:success' is not being called:
remove_tag: function(){
$view.on('ajax:beforeSend', '.cloud .remove-tag', function(event, xhr, settings) {
$('.loading').show();
})
$view.on('ajax:complete', '.cloud .remove-tag', function(event, xhr, settings) {
$('.loading').hide();
})
$view.on('ajax:success', '.cloud .remove-tag', function(event, xhr, settings) {
alert("It was a success!")
})
},
How can I invoke 'ajax:success' event?
Upvotes: 2
Views: 3574
Reputation: 9457
I figured out the problem. My goal was to remove the unnecessary use of something like this:
$.ajax({
type: "POST",
data: $form.serialize(),
url: $form.attr('action'),
beforeSend: function() {
$('.loading').show();
},
complete: function(){
$('.loading').hide();
$form.closest('.modal').modal('hide')
},
success: function(resp){
console.log(resp);
}
});
And by using the remote: true
on the link, the Rails link_to
helper generates a data-remote
attribute, which the rails.js file will add an event listener to for ajax requests, therefore removing the need for the jquery $.ajax()
method.
What I did not realize was that, the ajax call BY DEFAULT sets the dataType to 'script' and therefore the HTTP Request Headers Accept
header is set with 'application/javascript'. Thus, the content type in the response will be :js
(application/javascript). This requires you to either define a :action.js.erb
file or pass a block. But if you pass a block, it will be a block evaluated as JavaScript, not JSON or TEXT! Hence, when I was sending JSON back to the browser, it got confused. It was expecting to evaluate javascript, not parse JSON and thus a parse error would occur.
$view.on('ajax:beforeSend', '.cloud .remove-tag', function(xhr, settings) {
$('.loading').show();
})
$view.on('ajax:complete', '.cloud .remove-tag', function(xhr, status) {
$('.loading').hide();
})
// this only triggered if you actually destroy record
$view.on('ajax:success', '.cloud .remove-tag', function(data, status, xhr) {
$('.loading').hide();
// JSON.parse not needed; status already parsed as json
var data_id = JSON.parse(status['data-id']);
$('.cloud').find("li[data-id='"+data_id['data-id']+"']").remove();
})
$view.on('ajax:error', '.cloud .remove-tag', function(xhr, status, error) {
console.log( error );
})
Consequently, ajax:error
was being triggered and not ajax:success
.
When I changed the link to specify I wanted json as a response, then ajax:success
was triggered.
<%= link_to tagging, remote: true, method: :delete, class: 'remove-tag', :'data-type' => 'json' do %>
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
<% end %>
So why did I choose to return JSON and not just use format.js
and add a delete.js.erb
file? Because that means I would have to reference a DOM id or class in that js.erb file because I would have lost context to the module I created. By passing the data back to the event listener, I was able to continue to work with the closure created by the javascript functions.
Upvotes: 3
Reputation: 6321
I do not know, whats your $view
is, but example in below should be handle it.
$(document).ready(function() {
//form id
$('.remove-tag')
.bind('ajax:success', function(evt, data, status, xhr) {
//function called on status: 200 (for ex.)
console.log('success');
alert("It was a success!");
})
.bind("ajax:error", function(evt, xhr, status, error) {
//function called on status: 401 or 500 (for ex.)
console.log(xhr.responseText);
});
});
You have also 2nd option to handle it, just creating destroy.js.erb
file.
console.log("Coupon | destroy.js.erb file");
$('nav').after("<div class='alert alert-danger'> Successfully Destroyed </div>");
$(".coupon_" + <%= @coupon.id %>).fadeOut(250, function(){
$(this).remove();
});
Upvotes: 1