I'm calling a function, which makes an ajax GET to a url, like this:
// parameters = url, callback, boolean
that.mapUrl( window.location.search, function(spec) {
console.log("initial mapping done");
console.log(spec);
// do stuff
}, true);
mapUrl will trigger an Ajax request. Inside the Ajax done or success handler, I want to trigger my callback function, but doing it like this:
$.ajax({
method: 'GET',
url: obj[1],
context: $('body')
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("FAILED");
configuration = {
"errorThrown":errorThrown,
"textStatus": textStatus,
"jqXHR": jqXHR
}
}).done(function(value, textStatus, jqXHR) {
console.log("OK");
console.log(callback) // undefined!
configuration = {
"value":value,
"textStatus": textStatus,
"jqXHR": jqXHR
}
});
Question:
So I'm wondering how to pass my callback function into the ajax done-callback. Any idea how to do this?
Thanks!
EDIT
Here is the full mapURL function
that.mapUrl = function (spec, callback, internal) {
var key,
obj,
parsedJSON,
configuration = {"root" : window.location.href};
if (spec !== undefined && spec !== "") {
obj = spec.slice(1).split("=");
key = obj[0];
console.log(key);
switch (key) {
case "file":
$.ajax({
method: 'GET',
url: obj[1],
context: $('body')
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log("FAILED");
configuration = {
"errorThrown":errorThrown,
"textStatus": textStatus,
"jqXHR": jqXHR
}
}).done(function(value, textStatus, jqXHR) {
console.log("OK");
configuration = {
"value":value,
"textStatus": textStatus,
"jqXHR": jqXHR
}
});
break;
default:
// type not allowed, ignore
configuration.src = [];
break;
}
}
return configuration;
};
It would be generally better to preserve the "promise" interface rather than passing callbacks into your code. This will allow you to better trap error conditions.
function mapUrl(url) {
return $.ajax(...)
.fail(...)
.then(function(data) {
// preprocess data and return it
});
}
where using .then you can manipulate the returned data before it's passed to the callback:
mapUrl(...).done(function(data) {
// data has been preprocessed
...
});
If the AJAX call fails, you can chain additional .fail handlers at this point too, which your current API would not permit. This "separation of concerns" would let you put nicer error handling UI in place, for example, without cluttering your AJAX code with UI-related code.
Related
I have been trying to access some methods of the same class from an AJAX call inside one of the methods but it does not work. What could be the issue here? I get this error Uncaught TypeError: this.createTimeline is not a function see the comments on the code below!
//sequenceRender is the class and below is one method
sequenceRender.prototype.ajaxSequence = function(){
this.ajaxSequence = $.ajax('getSequence.php', {
dataType: 'json',
timeout: 2000
});
this.ajaxSequence.done(function (data, status, jqXhr) {
console.log(data)
this.SEQUENCE=data // I cannot access properties
this.createTimeline() // or methods from same class
this.createWells() // from inside here
})
this.ajaxSequence.fail(function (jqXhr, textStatus, errorMessage) {
console.log(errorMessage)
})
}
Looks like this is going to have different values as you are trying to call this inside a event
Try adding the changes below and let me know if this works for you
sequenceRender.prototype.ajaxSequence = function() {
mainContext = this;
this.ajaxSequence = $.ajax('getSequence.php', {
dataType: 'json',
timeout: 2000
});
this.ajaxSequence.done(function (data, status, jqXhr) {
console.log(data)
mainContext.SEQUENCE=data
mainContext.createTimeline() // call the 'this' from here
mainContext.createWells()
})
this.ajaxSequence.fail(function (jqXhr, textStatus, errorMessage) {
console.log(errorMessage)
})
}
Even though i am able to pass promise as parameter into error function, it does not trigger then & error function on the caller.?
if i change the code to put the AJAXError function inline, everything works fine.
Is it possible to pass around the promise around as parameter and still be able to trigger back then and fail on the caller up in the chain.
NOTE : promise is returned from palletizerService.addSerialNumberToPallet but but resolved/rejected in a different function AjaxFailed.
palletizerService.addSerialNumberToPallet ('p1','s1')
.then( function (response){
console.log('success');
},
function(error){
console.error(error);
});
Hereunder is the service that i am invoking from the above code
palletizerService.addSerialNumberToPallet = function (palletID, serialNumber) {
var deferred = $q.defer();
$.ajax({
url: url,
type: "GET",
dataType: "json",
methodName: methodName,
deferred: deferred.promise,
data: "palletID=" + palletID + "&serialNumber=" + serialNumber,
timeout: 20000, // 20 seconds for getting result, otherwise error.
error: AjaxFailed,
complete: function (jqXHR, textStatus) {
console.log("Ajax-finally : " + methodName);
},
beforeSend: function (qXHR, settings) {
},
success: function (data, textStatus, jqXHR) {
console.log("Ajax-Sucess " + methodName);
deferred.resolve(data);
}
});
return deferred.promise;
};
This is the external function
function AjaxFailed(qXHR, textStatus, errorThrown) {
try {
console.error('Ajax-Error in ' + methodName, qXHR.status, qXHR.responseText);
} catch (e) {
console.log("LocalMethod-Error in " + methodName);
console.error(e);
}
finally {
this.deferred.reject(qXHR);
}
}
Your problem is that you are passing the promise as the .deferred property of your options object. You cannot resolve/reject promises, they don't have a .reject method (and your code should throw when trying to call it). You would instead need to pass the deferred object you are holding.
However, this is a bad idea anyway. As jQuery.ajax does already return a promise, you should simply assimilate it using $q.when:
function (palletID, serialNumber) {
return $q.when($.ajax({
…
}));
}
(you will be able to omit all success and errror handlers, angular-promise does take care of them; if you want to add transformations or logging do those on the promise).
Yet that's still not optimal, in fact there hardly is a reason to use jQuery.ajax at all. Just use Angular's $http service, which also returns an angular promise right away.
I have written a small Jquery plugin that makes it easy for me to implement Facebook like "likes" on any item in my application. My only issue now is that I struggle to implement the success / error callback of my plugin.
$('.user-like').like({
success: function(data, textStatus, jqXHR) {
$(this).text('Liked');
}
});
My issue with the above code is this line:
$(this).text('Liked');
I'm aware of what why the issue happens, I just can't find a good way to make it work like I want it. Let me explain how the script works and what my Goal is:
As you can see I'm passing the call along to the likeApi() function that executes an AJAX call. Further you see that I merge my Options with the defaults and that you can override the success and error callback of the AJAX object.
The issue is now that this in the above code is the scope of the AJAX call and not my original method. I want to allow the user to define his own success / error callback that depends on the result of the API call and allows me to do something based on the state if it was a success or failure so that I can change the like text for example. How can I do this?
(function ($) {
$.likeApi = function (action, options) {
if (action != 'like' && action != 'unlike') {
return false;
}
var options = jQuery.extend({}, jQuery.likeApi.defaults, options);
$.ajax({
type: "POST",
url: options.baseUrl + action + '.json',
data: {
data: {
Like: {
foreign_key: options.id,
model: options.model
}
}
},
success: options.success,
error: options.error,
dataType: 'json'
});
};
$.fn.like = function (options) {
var scopedOptions = options;
this.on('click', function(event) {
event.preventDefault();
$.likeApi('like', $.extend({}, scopedOptions,{
'id': $(event.target).data('like-fk'),
'model': $(event.target).data('like-model')
}));
});
return this;
};
$.fn.unlike = function (options) {
var scopedOptions = options;
this.on('click', function(event) {
event.preventDefault();
var result = $.likeApi('unlike', $.extend({}, scopedOptions,{
'id': $(event.target).data('like-fk'),
'model': $(event.target).data('like-model')
}));
alert(result);
});
return this;
};
$.likeApi.defaults = {
baseUrl: '/likes/likes/',
action: null,
model: null,
id: null,
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus);
},
success: function(data, textStatus, jqXHR) {
alert(textStatus);
}
};
}(jQuery));
Two options: you can maintain context by adding a variable that references the original this, or you can use jquery.proxy()
Option 1:
Maintain the context by adding a variable that references the original this like so:
(function ($) {
$.likeApi = function (action, options) {
var self = this;
Then you just call self whenever you are out of context.
If you want to keep self available externally, you can inject it using jquery extend.
http://api.jquery.com/jquery.extend/
options.success = $.extend(options.sucesss, {el: self});
inside your ajax call
$('.user-like').like({
success: function(data, textStatus, jqXHR) {
$(data.el).text('Liked');
}
});
Option 2:
Alternatively, you can use jQuery.proxy()
http://api.jquery.com/jquery.proxy/
proxy can change the this context for you...
Does following call make sense / is it even possible?
getCall('GET', URL, null, function(x, status, jqXHR){ }, failedRes);
function getCall(method, url, data, func, func2){
.ajax({
type: method,
contentType: "application/json",
data: data,
url: url,
dataType: "json"
}).done(function(data, textStatus,jqXHR) {
console.log("done");
}).fail(function(jqXHR, textStatus, err){
console.log("fail");
});
}
function func2(jqXHR, textStatus, errorThrown){
window.alert("AJAX call error occured");
console.error( errorThrown );
}
I wonder most because of the "{}", but also because of the parameters.
Is the function-parameter "function(data, textStatus, jqXHR){ }" too much? Rather the {} not correct?
function(x, status, jqXHR){ } defines a function which can be called, but when it is called it doesn't do anything except returning undefined. In some situation, this might be intentional.
In this case I wonder why that parameter exists at all, because getCall doesn't even use the parameter func.
You can pass anything in. Personally, I like to set up data passing in a more structured manner just in case, in the future, things need to be updated. Passing function references or functions themselves is totally doable.
ala:
var data = {
call: "GET",
url: URL,
data: null,
func: function(x, status,jqXHR){},
func2: failedRes
}
getCall(data);
function getCall(data){
data = data || {};
// then just access your vars like so: data.url
Yes, this works.
function callFunction(func, param) {
return func(param);
}
callFunction(
function(a) {
return a*2;
},
2
);
returns 4
This is JavaScript AJAX request/response format:
function xhr(method, url, send, success){
var x = new XMLHttpRequest || new ActiveXObject('Microsoft.XMLHTTP');
var v = send ? encodeURI(send) : null;
method = method.toUpperCase();
x.open(method, url);
if(method.match(/^POST$/)){
x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
x.setRequestHeader('Content-length', v.length); x.setRequestHeader('Connection', 'close');
}
x.onreadystatechange = function(){
if(x.readyState === 4 && x.status === 200){
success(x.responseText);
}
}
x.send(v);
}
Creating a custom sync() method in backbone.
I would like to do this the "right" and interfere with Backbone's normal functions as little as possible.
This is the code that I have so far:
var CustomSyncModel = Backbone.Model.extend({
sync:function(method, model, options){
var params = {
type: 'POST'
url: model.url(),
error: function(jqXHR, textStatus, errorThrown){
alert('error');
},
success: function(data, textStatus, jqXHR){
model.parse(data);
}
};
// Got this from line 1359 in Backbone.js developement library
// version 0.9.2:
$.ajax(_.extend(params, options));
}
});
The issue that I am having is that the line: $.ajax(_.extend(params, options)); seems to be overwriting the custom success and error functions that I created. But I'm also concerned about interfering with any custom callbacks or other functionality that may have been specified elsewhere in the application that is using this model.
What is the "correct" way to go about overriding the Backbone's sync() method?
Thanks!
If you look at Model#fetch you'll see the usual approach that Backbone uses:
fetch: function(options) {
//...
var success = options.success;
options.success = function(resp, status, xhr) {
if (!model.set(model.parse(resp, xhr), options)) return false;
if (success) success(model, resp);
};
//...
}
So Backbone just replaces the function with a new one that calls the original. In your case, you'd have something like this:
// We don't own options so we shouldn't modify it,
// but we can do whatever we want to a clone.
options = _(options).clone()
// Replace options.error with a wrapper.
var error = options.error;
options.error = function(jqXHR, textStatus, errorThrown) {
alert('error');
if(error)
error(jqXHR, textStatus, errorThrown);
};
// Replace options.success with a wrapper.
var success = options.success;
options.success = function(data, textStatus, jqXHR) {
model.parse(data);
if(success)
success(data, textStatus, jqXHR);
};
// We don't need error or success in here anymore.
var params = {
type: 'POST',
url: model.url()
};
$.ajax(_.extend(params, options));
BTW, your model.parse(data); in your success handler probably doesn't do anything useful, parse should just be a simple filter so you'd want to do something (such as a model.set call) with the model.parse(data) return value.