I need a little help in best practices around "control flow" within javascript. In the below example my updateUI function is not properly working because my saveData function has not yet completed.
function save(data){
saveData(data);
updateUI();
}
For a temporary fix, I did this
function save(data){
if(saveData(data)){
updateUI();
}
}
Update: Here is my saveData method
function saveData(data){
$.ajax({
url: url, // a variable, not important for this example
data: data,
type: "post",
dataType: "json",
success: function(data) { return true; },
error: function(e) {
console.log(e);
}
})
I feel like I should learn the "standard" way, if such one exists.
Assuming that saveData is calling an AJAX function (e.g. $.ajax), have it return the result of that AJAX function:
function saveData() {
return $.ajax({
url: url, // a variable, not important for this example
data: data,
type: "post",
dataType: "json"
}).fail(function(e) {
console.log(e);
});
}
and then use jQuery deferred chaining to update the UI:
saveData().done(updateUI).fail(showError);
It allows you to avoid ever having to pass callback functions into your saveData call, which then allows you to completely separate your data saving logic from your UI logic.
EDIT I've updated the code to show how you can include a default fail handler also using deferred objects, but still return the promise so that you can handle your UI updating outside.
There are a few ways to do this.
You could provide a callback function to the saveData method -- which you would then call when saveData is complete.
var saveData = function(data, cb){
// do something with data to save it
cb.apply(this, ['arguments', 'as', 'an','array']); // or use .call instead
}
You could implement some events, so that your updateUI method listens for an event, which when it occurs it causes updateUI to run, which you fire when you're finished saveData. jQuery provides one, as well as Backbone.
There are a ton of ways to get this done -- choose the one that works best for you.
This is because your saveData is an a-synchronous method. You need to call updateUI() in the oncomplete event.
Have a look at this article and read up on promise()
Related
I have a javascript function that calls an AJAX, like this:
function addSquadronByID(id) {
$.ajax
({
type: "POST",
url: "server/get_squadron.php",
data: {
'id': id,
'counter': squadron_counter
},
cache: false,
success: function (data) {
squadron_counter++;
},
error: function () {
alert("AJAX error.");
}
});
}
}
Outside the document.ready, the variable is initialized like this var squadron_counter = 0;
This function perfectly works while I call it in the page, but if I try to use PHP to write it in the page, like this:
$list_squadrons = $DB->Execute($query);
while(!$list_squadrons->EOF){
$currentSquadron_id = $list_squadrons->fields["squadron_id"];
$currentSquadron_number = $list_squadrons->fields["squadrons"];
echo "addSquadronByID($currentSquadron_id);\n";
$list_squadrons->MoveNext();
}
The function writes into the document.ready() the correct calls, but squadron_counter is always zero, even if the function works. My only idea is that it works this way because javascript calls all the functions at once and does not wait to complete the first one before executing the second one, etc.. but how do I solve this?
HTML output as requested:
addSquadronByID(3, squadron_counter);
addSquadronByID(5, squadron_counter);
addSquadronByID(6, squadron_counter);
This is put into a
$( document ).ready(function() {
});
inside a <script> tag.
I think your idea about JS calling all functions without waiting for the first one to complete is in the right direction. This is called "asynchronous requests". Please refer to How to return the response from an asynchronous call? for a detailed explanation.
The idea is to send your 3 requests and then wait for all of them to complete before checking the value of your squadron_counter variable (or whatever data you have updated in your success callbacks).
Then if my understanding is correct, you do not know how to implement this waiting?
Since you are using jQuery, the implementation is super simple. Note first that your jQuery.ajax request returns a Deferred object. So simply keep a reference of the Deferred object created by each AJAX request you send. Then you could use for example jQuery.when with a callback in its then method to wait for all your requests to complete:
function addSquadronByID(id) {
return jQuery.ajax({ /* ... */ }); // returns a Deferred object.
}
var d1 = addSquadronByID(3),
d2 = addSquadronByID(5),
d3 = addSquadronByID(6);
jQuery.when(d1, d2, d3).then(
// callback executed on success of all passed Deferred objects.
function () {
console.log(squadron_counter);
}
);
Demo: http://jsfiddle.net/btjq7wuf/
I would like to wrap some jQuery functions (namely ajax) for several reasons, such as global configuration of parameters and, more importantly, being able to exchange it easily.
My question is, how can I "forward" the .done() and .fail() callbacks? (Which design-pattern is responsible for this behavior of jQuery?)
$.ajax({
url: "http://url.com",
})
.done(function( data ) {
// do something
})
.fail(function() {
alert( "error" );
});
For clarification, what I would like to be able to do is writing a function which wraps the above function and behaves like this:
myWrapper({config: "abc"}).done(function( data ) {
// do something
});
The $.ajax function does return a promise object which offers these methods (done, fail, always, most important then). Just return that object from your myWrapper function.
You need to return the ajax call if you want to do that...
function myWrapper(config) {
return $.ajax({
url: "http://url.com",
data: config
});
}
and then call like you suggested...
myWrapper({ config: "abc" }).done(function(data) {
// call is done
});
I've obviously made assumptions in the above code, but it should get the point across clearly enough. If not, just ask :)
Q1) I am using the tootltip from twitter bootstrap. I just noticed that its not working when the contents are added with ajax. After lots of googling, the solution seems to trigger the tooltip after ajax request. But in my case this is not possible because I am relying on the built in ajax API's for the framework. Is there any other work around?
$('.tootip').tooltip({placement:'left'});
Q2) In jQuery on() documentation, the usage is mentioned as
$(document).on(event, selector, function(){ //do stuff here })
So is this I have to do it?
$(document).on('ready', '.tootip', tooltip({placement:'left'}));
But its not working.
A1) One of the options/parameters you give to the ajax call is a callback function that triggered when the ajax call done and succeded. This success callback should initialize the tooltip.
For example, if you are using jQuery:
$.ajax({
url: 'your url'
success: function(result) {
// do your sruff here. Result holds the return data of the ajax call
}
});
A2) Look at the 3'rd parameter: function(){ //do stuff here }. You have to supply a function. Instead, what you have supplied is the result of invoking the function tooltip({placement:'left'}) which in this case returns an object and not a function. You should do:
$(document).on('ready', '.tootip', function() {
$('.tootip').tooltip({placement:'left'});
});
Update regarding your comment:
Inside a function, whether is a success callback or an event function, you can do whatever you like including call multiple functions:
$(document).on('ready', '.tootip', function() {
// Do many operations as much as you like here
func1();
func2();
});
$.ajax({
url: 'your url'
success: function(result) {
// Do many operations as much as you like here
func1();
func2();
}
});
Hope this helps!
I know this might sound silly but I have no idea how to do that …
$('#someButton').click(function(e) {
e.preventDefault();
// Do ajax stuff
triggerAjaxSubmit('#signUpForm', false);
modalSlide(modal, $('.loginTab'), $('.saveTab'));
});
On click on #someButton I want to submit an entire form via ajax. The function that does this is called triggerAjaxSubmit. This function has a simple $.ajax() function provided by jquery and does something on either success or error.
So what I wonder is how I can delay the next line modalSlide() until I get a success or error from the triggerAjaxSubmit() function. Right now triggerAjaxSubmit() gets fired and immediately after it the modalSlide() function fires. However I want the modalSlide() function only to fire once a success or error comes back.
Can I do that somehow? I mean like return "success" inside of the ajax() call and only than fire the modalSlide() function?
thank you in advance.
Kind regards,
Matt
edit:
function triggerAjaxSubmit(formId, reset, callback) {
$(formId).ajaxSubmit({
type: "POST",
cache: false,
resetForm: reset,
dataType: "text json",
success: function(jsonObject, status) {
// some other stuff
console.log('success');
},
error: function(requestObject, status) {
console.log('error');
}
});
}
triggerAjaxSubmit(form, true, function() {
console.log('this is not happning')
modalSlide(modal, $('.loginTab, .signUpTab'), $('.saveWeaveTab'), $('#newWeaveForm_title'));
});
You could return the deferred that $.ajax returns in triggerAjaxSubmit. Something like:
function triggerAjaxSubmit( .. ) {
return $.ajax({ .. });
}
and:
triggerAjaxSubmit( .. ).done(function() {
modalSlide( .. );
});
Edit: With the form plugin, you should be able to do this:
function triggerAjaxSubmit( .. , callback ) {
$( .. ).ajaxSubmit({
.. ,
success: function() {
console.log("success");
callback();
})
});
}
and:
triggerAjaxSubmit( .. , function() {
modalSlide( .. );
});
You need to call your modalSlide function from the callback in your $.ajax() call.
AJAX calls are asynchronous (unless you use the questionable async: false option in jQuery), so if you want to execute some code when the AJAX request receives a response, you'll need to put it in the success callback.
Depending on the rest of your code, this may be as simple as modifying the triggerAjaxSubmit function so that the callback supplied to the $.ajax() function includes the relevant code.
Or you may need to modify it a bit more to take a function as an argument, which is then provided as the success callback for the $.ajax() function.
Edit: I missed the "or error" in the question, hence why I only referred to the success callback. Same principle applies for the error callback, though.
call modalSlide function in success parameter of $.ajax() call
I currently write quite a bit of code in ASP.NET MVC and use jQuery quite a bit also to make ajax calls to Actions that return json.
I've started to evolve this pattern where I make an object below my global object that contains a 'success' and 'fail' callback along with a 'go' method that invokes the core logic and takes arguments... as follows:
var g1 = {}; // global object for some project...
g1.loadFileList = {
success: function (data, status, request) {
// notify success - or do nothing
},
fail: function (request, status, err) {
// notify #FAIL!
},
go : function (args) {
$.ajax({
url: '/Home/SomethingInterest',
type: 'POST',
success: this.success,
error: this.fail
});
}
};
$(document).ready(function() { g1.loadFileList.go({ whatever = 'data' }); });
I do this to keep my thoughts organized and I was about to start working on a slightly different pattern that I could start prototyping to reuse some of the logging I am doing in the error and success handlers, but then I thought... Is this in some other JS Fx???
Is it? Any thoughts on pre-existing frameworks that do similar things or thoughts on how to prototype this better is welcome.
I'm going to chalk this up to. This is just a pattern that I use sometime to encapsulate the async call with its success and fail response handlers.