I am working on a web application for debtor management and I am refactoring the code and try to adhere to the principle of separation of concerns. But the async nature of AJAX is giving me headaches.
From a jQuery dialog the user can set a flag for a debtor which is then stored in a database. If that succeeds, the dialog shows a notification. Until now I handled everything inside the jQuery Ajax success callback function: validating input, doing the ajax request and updating the content of the dialog.
Of course this lead to spaghetti code.
Thus I created a class AjaxHandler with a static method for setting the flag, which is invoked by the dialog. I thought that the dialog could update itself according the the return value of the AjaxHandler but I did not have the asynchronity in mind.
The following question was helpful in tackling the return values.
How do I return the response from an asynchronous call?
But how can I update the dialog without violating the SoC principle?
EDIT
$("#button").on("click", function() {
var returnValue = AjaxHandler.setFlag();
if(returnValue) { $("#div").html("Flag set"); }
else { $('#div").html("Error setting flag");
});
class AjaxHandler {
static setFlag(){
$.ajax({
type: "POST",
url: "ajax/set_flag.php",
success: function(returndata){
return returndata; //I know this does not work because of
//ASYNC,but that is not the main point.
}
}
})
There is many ways to handle async responses, but the jQuery way is slightly different, so when you are already using jQuery, handle it this way:
$('#button').on('click', AjaxHandler.setFlag)
class AjaxHandler {
static setFlag () {
this.loading = true
this
.asyncReq('ajax/set_flag.php')
.done(function () {
$('#div').html('Flag set')
})
.fail(function (err) {
$('#div').html('Error setting flag. Reason: ' + err)
})
.always(function () {
this.loading = false
})
}
asyncReq (url) {
return $.ajax({
type: 'POST',
url: url
})
}
})
Consider using events perhaps here?
$("#button").on("click", function() {
$('body').trigger('getdata', ["", $('#div')]);
});
$('body').on('getdata', function(event, datasent, myelement) {
var attach = event.delegateTarget;// the body here
var getAjax = $.ajax({
type: "POST",
url: "ajax/set_flag.php",
data: datasent // in case you need to send something
})
.done(function(data) {
$(attach).trigger('gotdata', [data, myelement]);
});
getAjax.fail(function() {});
})
.on('gotdata', function(event, datathing, myelement) {
myelement.html(!!datathing ? "Flag set", "Error setting flag");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
Note that inside those event handlers you could also call some function, pass a namespace for the function, basically do it as you please for your design.
Related
I have a project which involves live notification. So I stumbled upon using socket io but I didn't have enough time to learn it yet. So I tried doing it with AJAX and jQuery. Below is my code structure and I was wondering if this is gonna work with no drawbacks?
setInterval(function(){
if( !element.hasClass('processing') ){
element.addClass('processing');
$.ajax({
type: 'post',
dataType: 'json',
url: ajaxurl,
data: {},
success: function( response ){
/* Success! */
element.removeClass('processing');
}
});
}
}, 2500);
Some Extra Info
The way you described will work. From Experience I would just like to point out some things.
I usually do a recursive function, allows you to wait your interval between ajax calls and not a fixed rate. //OPTIONAL BUT DOES GIVE THE SERVER SOME BREATHING ROOM.
Use window.setTimeout() with an isActive flag. //ALLOWS YOU TO STOP POLLING FOR WHATEVER REASON, AND BECAUSE FUNCTION IS RECURSIVE START UP AGAIN IF NEED BE
For Sake of being thorough, I found it is always a good idea to handle the error case of the $.ajax() post. You could perhaps display some message telling the user he is no longer connected to the internet etc.
Some Sample Code:
var isActive = true;
$().ready(function () {
//EITHER USE A GLOBAL VAR OR PLACE VAR IN HIDDEN FIELD
//IF FOR WHATEVER REASON YOU WANT TO STOP POLLING
pollServer();
});
function pollServer()
{
if (isActive)
{
window.setTimeout(function () {
$.ajax({
url: "...",
type: "POST",
success: function (result) {
//SUCCESS LOGIC
pollServer();
},
error: function () {
//ERROR HANDLING
pollServer();
}});
}, 2500);
}
}
NOTE
This is just some things I picked up using the exact method you are using, It seems that Web Sockets could be the better option and I will be diving into that in the near future.
Please refer :
Jquery : Ajax : How can I show loading dialog before start and close after close?
I hope this could help you
$("div.add_post a").click(function(){
var dlg = loadingDialog({modal : true, minHeight : 80, show : true});
dlg.dialog("show");
$.ajax({
url : "/add.php",
complete : function (){
dlg.dialog("hide");
}
});
return false;
});
//--Loading dialog
function loadingDialog(dOpts, text = "пожалуйста подождите, идет загрузка...")
{
var dlg = $("<div><img src='/theme/style/imgs/busy.gif' alt='загрузка'/> "+text+"<div>").dialog(dOpts);
$(".ui-dialog-titlebar").hide();
return dialog;
}
I am making few ajax requests in my jQuery file. On success of these jQuery requests, I wrote few on click events which are not working.
This is my code
$(document).ready(function (){
$.ajax ({
type: "POST",
url: 'myServlet',
async: false,
success: function (response) {
id = parseInt(response);
setOutputEvents();
}
});
function setOutputEvents() {
for (var queryNumber = 0; queryNumber <= id; queryNumber++) {
$.ajax({
type: "POST",
url: 'myOtherServlet',
data: {queryNumber: queryNumber},
success: success,
async: false
});
var success = function (response) {
//some code here
generateTable();
}
}
}
function generateTable () {
//some code here
pagination();
}
function pagination(){
$(".class").click(function(event) {
alert();
});
}
$("#me").on("click", function(){
alert("me is triggered");
});
});
I understand making multiple ajax requests is a bad programming practice but what could be the reason for on click events not getting triggered?
These are the onclick events which are not working.
function pagination(){
$(".class").click(function(event) {
alert();
});
}
$("#me").on("click", function(){
alert("me is triggered");
});
I am using Google Chrome Version 39.0.2171.95 on Windows 7.
Please do let me know if any further information is necessary.
Since you use ajax to load even the initial content it seems, .class / #me html elements likely do not exist on initial page load of the DOM. As you didn't post html, i'm guessing this is the case.
Thus, you need to use a delegated event click handler to respond to it
so, you would change
$("#me").on("click", function(){
to
$(document).on("click", "#me", function(){
and so forth to link it to the parent element that does exist, the document itself.
This would work:
$(".class").on("click", function(){
alert("me is triggered");
});
function generateTable () {
//some code here
pagination();
}
function pagination(){
$(".class").trigger("click");
}
Some notes:
Event handler must be registered before triggering click.
Triggered click selector must match the class which has the click event registered.
Functions must be defined before the usage.
I'm trying to use the ajaxStop function in jquery but can't get it to fire, any ideas?
What I'm trying to do is loop through each anchor tag and then update some content inside it, from there I want to use the ajaxstop event to fire a script to reorganize the anchors based on the updates
Thanks for any help
jQuery(document).ready(function($) {
function updateUsers() {
$(".twitch_user").each(function(index, user) {
$.ajax({ url: "https://api.twitch.tv/kraken/streams/" + $(user).attr("id") + "?callback=?", success: function(d) {
if(d.stream) {
$(user).addClass("online");
$(user).removeClass("offline");
$(user).children(".viewers").text(d.stream.viewers + " viewers");
} else {
$(user).addClass("offline");
$(user).removeClass("online");
$(user).children(".viewers").text("0 viewers");
}
console.log(d);
}, dataType: "json"});
});
}
//$(document).ajaxStart(function() {
// console.log("Event fired!");
// updateUsers().delay(2000);
//})
$(document).ajaxStop(function() {
console.log("Event fired!");
// updateUsers().delay(2000);
});
updateUsers();
});
Apparently the global handlers are turned off when doing JSONP requests, as explained in this ticket:
JSONP requests are not guaranteed to complete (because errors are not caught). jQuery 1.5 forces the global option to false in that case so that the internal ajax request counter is guaranteed to get back to zero at one point or another.
I'm not sure if JSONP is your intention or not, but the ?callback=? on the end of the URL makes jQuery handle it as such.
The solution was to set the following:
jQuery.ajaxPrefilter(function( options ) {
options.global = true;
});
I want to execute a piece of javascript after the ajax response has been rendered. The javascript function is being generated dynamically during the ajax request, and is in the ajax response. 'complete' and 'success' events to not do the job. I inspected the ajax request in Firebug console and response hasn't been rendered when the complete callback executes.
Does not work:
function reloadForm() {
jQuery.ajax({
url: "<generate_form_url>",
type: "GET",
complete: custom_function_with_js_in_response()
});
};
ajaxComplete does the job, but it executes for all the ajax calls on the page. I want to avoid that. Is there a possible solution?
$('#link_form').ajaxComplete(function() {
custom_function_with_js_in_response();
});
you can also use $.ajax(..).done( do_things_here() );
$(document).ready(function() {
$('#obj').click(function() {
$.ajax({
url: "<url>"
}).done(function() {
do_something_here();
});
});
});
or is there another way
$(document).ready(function() {
$('#obj').click(function() {
$.ajax({
url: "<url>",
success: function(data){
do_something_with(data);
}
})
});
});
Please, utilize this engine for share your problem and try solutions. Its very efficient.
http://jsfiddle.net/qTDAv/7/ (PS: this contains a sample to try)
Hope to help
Checking (and deferring call if needed) and executing the existence of the callback function might work:
// undefine the function before the AJAX call
// replace myFunc with the name of the function to be executed on complete()
myFunc = null;
$.ajax({
...
complete: function() {
runCompleteCallback(myFunc);
},
...
});
function runCompleteCallback(_func) {
if(typeof _func == 'function') {
return _func();
}
setTimeout(function() {
runCompleteCallback(_func);
}, 100);
}
Can't help a lot without code. As an general example from JQuery ajax complete page
$('.log').ajaxComplete(function(e, xhr, settings) {
if (settings.url == 'ajax/test.html') {
$(this).text('Triggered ajaxComplete handler. The result is ' +
xhr.responseHTML);
}
});
In ajaxComplete, you can put decisions to filter the URL for which you want to write code.
Try to specify function name without () in ajax options:
function reloadForm() {
jQuery.ajax({
url: "<generate_form_url>",
type: "GET",
complete: custom_function_with_js_in_response
});
};
Sometimes in my application there are many elements loading so I want to show the typical AJAX spinner above the control (or DOM node) with it disabled.
What is the easiest/best way to do that?
Ideally I would like to:
$("#myelement").loading();
$("#myelement").finishloading();
Or even better being able to do AJAX requests directly with the element:
$("#myelement").post(url, params, myfunction);
Being #myelement a regular node or form input.
You could use beforeSend and complete callbacks:
$.ajax({
url: 'script.cgi',
type: 'POST',
beforeSend: function() {
$('.spinner').show();
},
complete: function() {
// will trigger even if request fails
$('.spinner').hide();
},
success: function(result) {
// todo: do something with the result
}
});
Since you're already using jQuery, you may want to look into BlockUI in conjunction with Darin Dimitrov's answer. I haven't used it yet myself as I just came across this today, but it looks decent.
If you're writing a semi-large-ish application and anticipate making many AJAX calls from different places in your code, I would suggest that you either add a layer of abstraction over $.ajax, or create a helper function to avoid having boiler plate for your UI indicator all over the place. This will help you out a lot should you ever need to change your indicator.
Abstraction method
var ajax = function(options) {
$.ajax($.extend(
{
beforeSend: function() {
$.blockUI();
},
complete: function() {
$.unblockUI();
}
},
options
));
};
ajax({
url: 'script.cgi',
type: 'POST',
success: function(result) {
// todo: do something with the result
});
Helper method
var ajaxSettings = function(options) {
return $.extend(
{
beforeSend: function() {
$.blockUI();
},
complete: function() {
$.unblockUI();
}
},
options
);
};
$.ajax(ajaxSettings({
url: 'script.cgi',
type: 'POST',
success: function(result) {
// todo: do something with the result
}
}));
Also, I wouldn't suggest overwriting the $.ajax method itself.
what i've done in the past is, on post pass the element id (a containing div) to a function which replaces it's inner HTML with a loading image, and then in the post back replace it's content again with the updated real content.
If you want to show the spinner every when an ajax call is in progress I think you should use ajaxStart and ajaxStop.
$("#spinner")
.ajaxStart(function(){$(this).show();})
.ajaxStop(function(){$(this).hide();});