Why does AngularJS $http .success promise work but .then doesn't? - ajax

I've been using the following code for doing ajax calls in Angular which worked fine so far:
function getData (url) {
var deferred = $q.defer();
$http.get(url, {
cache: true
}).success(function (data) {
deferred.resolve(data); // works
});
return deferred.promise;
}
As you can see there is only a success handler. When I wanted to add an error handler, I noticed that the docs state that success and error were depracated and should not be used any more (https://docs.angularjs.org/api/ng/service/$http). Instead the .then promise should be used, so I changed my code to this:
function getData (url) {
var deferred = $q.defer();
$http.get(url, {
cache: true
}).then(function (data) {
deferred.resolve(data); // Not called :(
}, function () {
deferred.resolve(false); // Not called :(
});
return deferred.promise;
}
Now everything stopped working. .then is never called. Why? I'm on latest Angular version 1.4

Ok, I found the problem. Actually, there were two problems.
First, I had a redundant promise which I removed:
function getData (url) {
return $http.get(url, {cache: true});
}
Second, the returned data from .then seems to be a little different from .success. Instead of returning the plain result data, it returns an object with some meta data from the call. So, in order to get the data, I'd have to access data.data:
pub.getList = function () {
return getData(serviceurl + "?c=latest").then(function (result) {
var list = result.data;
precacheArticles(list);
return list;
});
};

Related

Knockout: How to put $.get into a function?

I am using getJSON in my knockout view:
$.getJSON("/GetItems", function (data) {
self.Items(data.map(viewModelFromData));
});
I want to make it a function for reuse so next time I can reload items on page action. How to do it?
When I tried:
self.getBasketItems = $.getJSON("/umbraco/Surface/Booking/GetBasketItems",
function(data) {
self.Items(data.map(viewModelFromData));
// or return data.map(viewModelFromData);
});
I got self.getBasketItems() is undefined.
The quickest fix:
self.getBasketItems = function() {
return $.getJSON(
"/umbraco/Surface/Booking/GetBasketItems",
function(data) {
self.Items(data.map(viewModelFromData));
});
};
This returns the promise, so you can use it in a chain like so:
self.getBasketItems().then(function() { console.log(self.Items()); })
You can also make a more general utility function like:
const getTransformWrite = (url, converter, target) =>
() =>
$.getJSON(url)
.then(converter)
.then(target);
}
}
self.getBasketItems = getTransformWrite(
"/umbraco/Surface/Booking/GetBasketItems",
data => data.map(viewModelFromData),
self.Items
);
I don't really think this is an improvement, but it might suit your style.
Make sure you don't forget to handle exceptions/errors!
what #haim770 suggested was this:
self.getBasketItems = function() {
return $.getJSON(
"/umbraco/Surface/Booking/GetBasketItems",
function(data) {
self.Items(data.map(viewModelFromData));
});
};
But, from your comments, it seems you're trying to actually return the value into self.getBasketItems? In that case, you will need to do a synchronous call (getJSON is asynchronous) using the $.ajax method instead.
self.getBasketItems = function() {
var _r;
$.ajax(
dataType: "json",
async: false,
url: "/umbraco/Surface/Booking/GetBasketItems",
success: function(data) {
_r = self.Items(data.map(viewModelFromData));
});
return _r
};
Please note that this second solution is very quirky. And the browser will actually hang waiting for the response from the server. You would be better of using either callbacks or promises.
Let me know if this helps!
Cheers,

mootools ajax request aborts sporadically (onFailure)

I have a javascript function called when the page loads. It just gets some user data through an ajax call and checks some checkboxes based on the user id's returned.
window.onload=function(){
getBlockedUsers();
}
function getBlockedUsers(){
var deptID = $('deptID').value;
var request_data = 'DeptID='+deptID;
var req = new Request.JSON({
url:'/ajax/getuserData.cfm',
method: 'post',
noCahe: true,
data: request_data,
onSuccess: function(response)
{
var jLength = response.json.length;
for(i=0;i<jLength;i++){
var user_id = response.json[i].id;
if($('user_'+user_id)){
$('user_'+user_id).checked = true;
}
}
},
onFailure: function (xhr)
{
alert('There was an error while trying to fetch data');
},
onException: function (xhr)
{
alert('There was an exception while trying to fetch data');
}
}).send();
}
I keep getting the alert in the onFailure() function for some reason and when I inspect the ajax call, it appears to be aborted. This does not happen all the time. Happens in IE9.
I had the function call within domReady but moved to onload thinking it would help but it's still the same. Any ideas on why this might be happening would really help.

What does an asynchronous AJAX call return?

I am trying to create a test case to monitor progress of multiple parallel asynchronous server tasks. I have the code sort-of working, but there are several pieces I don't understand. First, what does the $.ajax call below return? In theory, it should return undefined, but that doesn't seem to be the case.
function doParallel() {
var promiseA, promiseB, handleSuccess, handleFailure;
var dataA = JSON.stringify({ size: a });
var dataB = JSON.stringify({ size: b });
promiseA = $.ajax({
url: testGlobal.urlA,
data: dataA,
type: "POST",
async: true,
contentType: 'application/json; charset=utf-8',
dataType: "json",
success: function (rtnData) {
// Get the result
result = (rtnData === undefined) ? null : $.parseJSON(rtnData.d);
},
error: function (xhr, textStatus, errorThrown) {
// Whoops! didn't work
reportAjaxError(xhr, textStatus, url, data);
},
complete: function (xhr, textStatus) {
// Errors have already been handled, so only
// deal with success cases
}
}); <--- WHAT GETS RETURNED TO PROMISE HERE?
... (same code for promiseB, etc.
var notifyingPromiseA = intervalPromise(2000, 'a');
var notifyingPromiseB = intervalPromise(2000, 'b');
...
promiseA.done(function() {
log("A done");
}
promiseB.done(function() {
log("B done");
}
$.when(promiseA, promiseB).done(function() { log ("All done") });
}
function intervalPromise(millis, source) {
var deferred = $.Deferred();
//checkProgress();
log("Checking progress on " + source);
var id = setInterval(function () {
deferred.notify();
if (testGlobal.aDone && testGlobal.bDone) {
clearInterval(id);
deferred.resolve();
}
}, millis);
return deferred.promise();
}
...
$.ajax() returns the XMLHttpRequest object. As of jQuery v1.5, $.ajax() also implements and returns a Promise / Deferred interface.
http://api.jquery.com/jQuery.ajax/
http://api.jquery.com/category/deferred-object/
With a Promise, you can chain additional callbacks based on the results of the original ajax call.
// setup interval / timer to update UI not finished / still working logic
$.ajax().done(function() {
// clear UI not fninshed / still working logic
});
mmm.. official docs say:
The $.ajax() function returns the XMLHttpRequest object that it
creates. Normally jQuery handles the creation of this object
internally, but a custom function for manufacturing one can be
specified using the xhr option. The returned object can generally be
discarded, but does provide a lower-level interface for observing and
manipulating the request. In particular, calling .abort() on the
object will halt the request before it completes.
http://api.jquery.com/jQuery.ajax/
As of jQuery 1.5, jQuery.ajax() (and various ajax shortcut methods) returns a jqXHR object, which is a superset of the browser's native XMLHttpRequest object and implements inter alia the Promise interface.
Read more about the jqXHR object here.

Wait for Ajax call to finish

I have a situation where in I m doing a number of AJAX calls using jquery and in turn returning JSON data from those calls into some variables on my page.
The issue is that the Ajax call takes a little time to get processed and in the mean time my control shifts to next statement where I intend to use the output of AJAX call.
Since the call takes time to return the data I am left with empty object that fails my function.
is there any way where I can wait for the finish of AJAX call to happen and proceed only when the result is returned from the call???
so this is my code where in I am trying to return transactionsAtError to some other jquery file where the control shifts to next statement before this call gets executed
this.GetTransactionAtErrors = function (callback) {
var transactionsAtError;
$.ajax({
url: ('/Management/GetTransactionsAtError'),
type: 'POST',
cache: false,
success: function (result) {
if (result && callback) {
transactionsAtError = (typeof (result) == "object") ? result : $.parseJSON(result);
}
}
});
return transactionsAtError;
}
Assuming you are using jQuery's $.getJSON() function, you can provide a callback function which will be executed once the data is returned from the server.
example:
$.getJSON("http://example.com/get_json/url", function(data){
console.log("the json data is:",data);
});
EDIT:
After seeing the code you added i can see what's your problem.
Your return transactionsAtError; line runs independently of the ajax call, i.e it will run before the ajax is complete.
you should just call your callback inside your success: function.
example:
this.GetTransactionAtErrors = function (callback) {
$.ajax({
url: ('/Management/GetTransactionsAtError'),
type: 'POST',
cache: false,
success: function (result) {
if (result && callback) {
var transactionsAtError = (typeof (result) == "object") ? result : $.parseJSON(result);
callback(transactionsAtError);
}
}
});
}
When you have your result in scope you can check wait for ongoin ajax calls to finish by using es6 promise:
function ajaxwait()
{
return(new Promise(function(resolve, reject) {
var i = setInterval(function() {
if(jQuery.active == 0) {
resolve();
clearInterval(i);
}
}, 100);
}));
}
You can use this like.
ajaxwait().then(function(){ /* Code gets executed if there are no more ajax calls in progress */ });
Use an es6 shim like this https://github.com/jakearchibald/es6-promise to make it work in older browsers.

Retain the context of JQuery ajax function

I have an old code which is dependant on JQuery 1.3.2 that uses the following ajax call
function ChangeContent(url, somepageobject) {
var xhrobj = $.ajax({
url: url,
context: somepageobject,
callback: doFurtherStuff,
success: function(data) {
somepageobject.html($(data));
this.callback.call(this.context[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}
Problem with the code above is that this.callback is null when I upgraded to JQuery 1.8.1, most importantly the ChangeContent function is being used in different places and is outside my control (its used as as an API for external users...etc). An example of the usage of the above is like this:
xhr_object = ChangeContent("/someurl, $("#resultContainer"));
function doFurtherStuff(responseText, statusText, XMLHttpRequest)
{
var identifier = '#' + this.id;
...
}
Notice that the doFurtherStuff must have the correct "this" object value which is the context specified in ChangeContent function. When I tried to use different deferred then() ...etc. functions in JQuery 1.8.1 to solve the above this.callback.call(this.context[0], data); problem after the upgrade the "this" object in the callback function had different value since I guess the new JQuery library handles that differently.
Is there anyway to fix the error above while limiting the change to ChangeContent function only as I try to avoid asking all users to change the way they call and handle call backs from that function?
When you add the context option, you are telling jQuery what this should be inside of the success callbacks. That means you can't access the options passed into the ajax request. Either don't supply a context, or pass in the callback manually.
function ChangeContent(url, somepageobject) {
var callback = doFurtherStuff;
var xhrobj = $.ajax({
url: url,
context: somepageobject,
success: function(data) {
somepageobject.html($(data));
callback.call(this[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}
Update:
If you want to instead continue using your code as-is, simply rename the context property.
function ChangeContent(url, somepageobject) {
var xhrobj = $.ajax({
url: url,
thecontext: somepageobject,
callback: doFurtherStuff,
success: function(data) {
somepageobject.html($(data));
this.callback.call(this.thecontext[0], data, "ok"); // >> Code breaks here
}
});
return xhrobj;
}

Resources