RAILS 6 Ajax events and Ajax Spinner - ajax

I am trying to show a loading animation / spinner on every Ajax request
my application.js
$(document).on("turbolinks:load", function() {
window.addAjaxLoaderHandler();
});
window.addAjaxLoaderHandler = function() {
$(document).on('ajax:send', function() {
$('#ajax-loader').show();
});
$(document).on('ajax:complete', function(){
setTimeout(() => {$('#ajax-loader').hide();}, 100);
});
}
This works perfectly, UNTIL I load a remote form by AJAX. If I submit that newly loaded form the ajax:send fires, but after completion (without any errors) the ajax:complete does not (the spinner will not be hidden).
The problem seems to be that I remove the loaded form with the ajax call.
What can I do to make this work?
I am just trying to click a link, load a form and remove the form after sending its information.
UPDATE
My application.html.haml (I use HAML so syntax is accordingly, so #... means <div id="...">#all indented code lines#</div>)
#main
= yield
#ajax-loader
The form will be loaded like:
$('#main').append('<%= j(render(:partial => 'new', :locals => {:model => #model})) %>');
The problem is that #ajax-loader is not hidden and still shows after form is submitted.
I think the problem is, that I remove the AJAX-call triggering element. But I was hoping, that since I bound the listener to document, that it still triggers.
Of course in this case I just can do $('#ajax-loader').hide();, but I am trying to understand why ajax:complete is not fired.

I guess you answered your own question: the problem is that when you remove the form, so does the event listener.
You can check what event listeners still apply to your document object using
getEventListeners(document). Try this on your console* after the spinner is fired and refuses to hide.
*edit: this is a Google Chrome function, might not work in other browsers, though most of them have ways to inspect the listeners on a node
I think of these workarounds:
a) Rebinding the listeners everytime you remove a form;
b) Attaching the listener to the window object instead of document
c) If you are using jQuery Ajax you could use the beforeSend and complete properties to show and hide the spinner instead of events. If it's not jQuery Ajax there's probably a similar way to achieve the same behavior.

Related

Wiring up Ajax.BeginForm that didn't exist during doc.ready?

I have a MVC page that loads a Partial via a Ajax.ActionLink, which works, and then the loaded Partial contains a form that has Ajax.BeginForm. This form is not getting wired up to unobtrusive ajax, and instead is performing a page refresh(I verified this in the Network log of the browser that shows the initiator when I click submit is the browser instead of jquery).
What I believe is the issue is that since the form didn't exist when the page is loaded(but later is added via the Ajax.ActionLink), then unobtrusive ajax didn't see the data-ajax attributes on the newly added form and wire up the necessary events. I'm assuming that only happens at document.ready, and the ajax form didn't exist then.
Is there something I can do to say "hey Unobstrusive Ajax, please look at my page again now that I have some new elements that are marked with data-ajax and wire them up"?
Thanks.
Looking at the unobtrusive ajax source, it has this:
$("form[data-ajax=true]").live("submit", function (evt) {
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault();
...
The form tag generated looks like this:
<form action="/Path/Create" class="form-horizontal" data-ajax="true" data-ajax-method="post" data-ajax-mode="replace" data-ajax-update="#ParentContainer" id="PathForm" method="post" novalidate="novalidate">
As far as I can tell the selector on the .live event should be picking up on the new form when it's loaded onto the page. The form is inside a bootstrap modal however, so I don't know if that would be preventing the event from bubbling up somehow.
I can even run this in Chrome console:
$("form[data-ajax=true]").live("submit", function (evt) {
var clickInfo = $(this).data(data_click) || [];
evt.preventDefault(); });
And it returns the form element successfully, but when I click submit it still does a full page refresh. I would expect it to at least do nothign since I wired it up to preventDefault.
To answer my own question regarding unobtrusive ajax and dynamically loaded content, it should work fine out of the box. The way it wires up to events with .live(deprecated, still works currently) should detect forms that are loaded dynamically onto the page. So apparently it doesn't suffer the same problem that unobtrusive validation does.
Our problem was unrelated to that. We were using bootstrap modal which spawned a modal div from inside a form. Since the modal then loaded another page containing a form, we realized we had a form within a form (even though it didn't really look that way since the other form was in a modal).
To solve this we moved the declaration of the modal div in the first form outside of the form. We could still have a link that referenced the modal to show it, but not the modal's inner form wouldn't be nested in the first form.
I believe the reason this caused both the .live and .on methods to not catch the event was because they depend on the event to bubble up to the document, and it bubbled up only as far as the outer form which was not an ajax form, thus did not match the selector.
Another solution would have been to write .on more like this so that the event would be caught when it bubbled up to the container of the inner form, instead of scoping it to the document where the event would reach the outer form first.
$('#innerModalId').on("submit", "form[data-ajax=true]", function (evt) {
evt.preventDefault();
...
return false
});
However since this was part of a library, that's not an ideal solution as I would have needed to repeat their code. We didn't get as far as updating unobtrusive ajax:
http://nuget.org/packages/jQuery.Ajax.Unobtrusive
But I think we would have still had the problem since even with .on it still didn't work due to our page structure problem.

jQuery load() event not firing after loading content via AJAX

I have some code which does the following:
External content is loaded via AJAX (video thumbnail images)
The new content is then inserted into a div using $("#content").append();
A mobile touch scrolling helper (iScroll) is applied to this div.
However the jQuery "load" event is not firing when the DOM changes due to an AJAX event, which means the call to initialise the scroller is happening too soon (before the images inthe content has loaded) which means it often doesn't get intiiallised. Without waiting for the images to load the content box is often short enough such that a scroll function isn't needed, but then when the images subsequently load, the box is not scrollable.
$("#videoList").append(videoThumbnails);
$(document).load(function () {
// doesn't fire
initScroller();
});
It appears that jQuery's append function does not block until all images referenced in the appended HTML have loaded.
How can I detect that all of the images loaded by the AJAX function have finished loading in order to call the initScroller() function AFTER all images have loaded?
OK I've found the solution in another similar question. It turns out there's a jQuery waitForImages plugin which does exactly what I want:
So I can just do this:
$("#videoList").waitForImages(function () {
// Fires when all images in the #videoList div have loaded
initScroller();
});
The methods you are trying to use are triggered only once, when the page is loaded, but not for changes you make to the DOM aftewards (e.g. inserting content with ajax).
If you want to observer DOM changes you can use the DOMNodeInserted event
$(document).bind("DOMNodeInserted", function(event) { ....do stuff...here });
But generally it would be better to trigger this with the ajax callback.
$('#targetElementForYourContent').load('server/url.html', function() {
...do stuff here....
});

Loading forms with .load kills the submit button in Firefox

I am currently loading several forms into a webpage with:
$(document).ready(function () {
$('#content').load('php_script.php', function() {
$(this).find('#someForm').ajaxForm(function() {
alert('Success!');
});
$(this).find('.someOtherForm').ajaxForm(function() {
alert('Success!');
});
});
});
This works in Chrome, Chromium and IE who loads the forms and everything works as it should (Clicking submit sends a request to the php-script defined in the form's action, which adds stuff to a db, and shows the alert dialog). In Firefox (v10.0.2) this code loads the forms into the DOM and displays them, but when clicking submit on any of the forms nothing happens.
At first I suspected ajaxForm, but changing the above code to:
$(document).ready(function () {
$('#content').load('php_script.php');
});
yields almost the same result, the difference being that the user is sent to the script defined as the action (Except for Firefox, where nothing happens).
How do I make Firefox not kill the submit button?
I solved it, bad HTML from my side:
<table><form ...>
<tr>...</tr>
</form></table>
Instead it should look like:
<form ...><table>
<tr>...</tr>
</table></form>
The validator did not catch this since it was loaded via jQuery (and I forgot to validate the page serving the forms), and Firefox buggered out.
The code above looks ok to me...
Have you had a look in firebug if there are any errors? Maybe there is a conflicting Id or something.
Maybe the form isnt completely loaded into the dom yet, might be worth giving live binding a try
Found this in the docs:
...jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as , , or elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser...
If you inspect the form is it the same as in other browsers?

MVC3 WebGrid: When sorting or paging, is there a way to call a javascript method BEFORE the Controller Action method is called?

I've been using this link as a reference for developing my WebGrid (http://msdn.microsoft.com/en-us/magazine/hh288075.aspx).
Currently what is happening is that my WebGrid is loaded, and I'm able to asynchronously page and sort just fine...no problems. What is an irritation is that once I click to page or sort, the user isn't aware that anything is happening.
So what I'm looking for is a way to call a javascript function (or anything really) before the controller's action method is called, so that I have something appear to let the user know work is being done to return their next page, sort, and so forth.
I'm not sure if I'm just missing something, but any help would be appreciated.
You could use the .ajaxSend() and .ajaxComplete() methods to show and hide some spinner during the AJAX requests:
$(function() {
$('#grid').ajaxSend(function () {
// this will be called before the AJAX request is sent
// here you can show some spinner
$('body').append('<div id="spinner">Loading ...</div>');
}).ajaxComplete(function () {
// this will be called after the AJAX request completes and
// could be used to hide the spinner
$('#spinner').remove();
});
});

jQuery Showing an Ajax loader during transmission & Prevent Multiple Submits

I have an app that has several different types of form elements which all post data to the server with jQuery AJAX.
What I want to do is:
Show a loader during AJAX transmission
Prevent the user from submitting twice+ (clicking a lot)
This is easy to do on a one off basis for every type of form on the site (comments, file upload, etc). But I'm curious to learn if that is a more global way to handle this?
Something that's smart enough to say:
If a form is submitting to the server and waiting for a response, ignore all submits
Show a DISABLED class on the submitted / clicked item
Show a loading class on the class="spinner" which is closest to the submit item clicked
What do you think? Good idea? Done before?
Take a look at the jQuery Global Ajax Event Handlers.
In a nutshell, you can set events which occur on each and every AJAX request, hence the name Global Event Handlers. There are a few different events, I'll use ajaxStart() and ajaxComplete() in my code sample below.
The idea is that we show the loading, disable the form & button on the ajaxStart() event, then reenable the form and hide the loading element inside the ajaxComplete() event.
var $form = $("form");
$form.ajaxStart(function() {
// show loading
$("#loading", this).show();
// Add class of disabled to form element
$(this).addClass("disabled");
// Disable button
$("input[type=submit]", this).attr("disabled", true);
});
And the AJAX complete event
$form.ajaxComplete(function() {
// hide loading
$("#loading", this).hide();
// Remove disabled class
$(this).removeClass("disabled");
// Re-enable button
$("input[type=submit]", this).removeAttr("disabled");
});
You might need to attach to the ajaxError event as well in case an AJAX call fails since you might need to clean up some of the elements. Test it out and see what happens on a failed AJAX request.
P.S. If you're calling $.ajax or similar ($.getJSON), you can still set these events via $.ajaxStart and $.ajaxComplete since the AJAX isn't attached to any element. You'll need to rearrange the code a little though since you won't have access to $(this).
I believe you have to do 2 for sure and 3 to improve usability of your app. It is better to keep backend dumb but if you have a security issue you should handle that too.

Resources