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 :)
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/
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 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()
If I use a closure to define something is there a means of waiting so to speak until the variable is populated before moving on to the next bit.
Example:
var myVari = someFunction();
$.each(myVari, function(){/*code for each*/});
the function that defines myVari is an AJAX call, which can take a second or 4 (yea its not to fast) to define the variable. Problem is, before the AJAX call yields its results the $.each has already fired off and errored due to myVari being empty. Is there a better way to approach this scenario?
You should adapt your code so that you can pass a callback to someFunction, which you execute when the AJAX call is completed.
The only way you can wait for the AJAX call to complete is to change the call to synchronous, but this is heavily discouraged as it locks up the browser completely for the duration of the AJAX call.
Because you are already using the jQuery libary, this process of callbacks becomes a whole lot easier. Instead of returning the variable like you are at the moment, I'd return the jQuery AJAX object (which has a promise interface as of 1.6), so you can easily add callbacks to it:
function someFunction () {
return jQuery.ajax('some/url.php', {
// whatever
});
}
var myVari = someFunction();
myVari.done(function (data) {
$.each(data, function(){/*code for each*/});
});
If I understand what you are trying to do, then you could try your $.each inside the 'success' handler of your ajax call.
Rewrite someFunction to something like -
var myVari; //define this here or in whichever calling scope where it needs to be available.
$.ajax({
'url': 'http://..',
'type': 'GET', // or POST
'data': { } // whatever data you need to send
'success': function(data) {
myVari = process_the_server(data);
$.each(myVari, function() {...});
}
});
Use a callback, like this:
someFunction(function(myVari) {
$.each(myVari, function(){ /*code for each*/ });
});
Then redefine someFunction like this:
function someFunction(callback) {
var myVari;
/* ... */
/* calcuate myVari */
/* ... */
/* instead of returning it, pass it to the callback: */
callback(myVari);
}
The correct way is: Instead of running the each on its own, run it inside the ajax call.
You could, I suppose do:
function checkFunc() {
setTimeout(function() {
if(myVari) {
$.each(........);
} else {
checkFunc();
}
}, 1000);
}
That not really good coding practice, but it will work.
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.