So I was reading through the code on Malsup's twitter plugin and I noticed he'd written his own method to handle jsonp but with timeouts and errors. I can only assume the built in jQuery method 'getJSON' doesn't have this functionality even though it clearly works fine.
So, should I continue to use Malsups version in my projects where I'm making JSONP requests or just stick with jQuery's method. I have emailed Malsup and Paul Irish to ask about why it was necessary to write this but I didn't hear back. Can't blame 'em really:)
$.getJSONP = function(s){
s.dataType = 'jsonp';
$.ajax(s);
// figure out what the callback fn is
var $script = $(document.getElementsByTagName('head')[0].firstChild);
var url = $script.attr('src') || '';
var cb = (url.match(/callback=(\w+)/) || [])[1];
if (!cb)
return; // bail
var t = 0, cbFn = window[cb];
$script[0].onerror = function(e){
$script.remove();
handleError(s, {}, "error", e);
clearTimeout(t);
};
if (!s.timeout)
return;
window[cb] = function(json){
clearTimeout(t);
cbFn(json);
cbFn = null;
};
t = setTimeout(function(){
$script.remove();
handleError(s, {}, "timeout");
if (cbFn)
window[cb] = function(){
};
}, s.timeout);
function handleError(s, o, msg, e){
// support jquery versions before and after 1.4.3
($.ajax.handleError || $.handleError)(s, o, msg, e);
}
};
If it's JSONP, you can use
$.getJSON(url + "&callback=?", [args]);
to get JSONP and call a function when it loads. The &callback=? query lets jQuery generate a random callback function in the global scope to respond to the JSONP call.
From the jQuery docs for getJSON(...)
Important: As of jQuery 1.4, if the
JSON file contains a syntax error, the
request will usually fail silently.
Avoid frequent hand-editing of JSON
data for this reason. JSON is a
data-interchange format with syntax
rules that are stricter than those of
JavaScript's object literal notation.
For example, all strings represented
in JSON, whether they are properties
or values, must be enclosed in
double-quotes. For details on the JSON
format, see http://json.org/.
Presumably, silent failure was not something they liked, hence the plugin. In your case, if you're making JSONP requests and find yourself using the onError, or onTimeout methods, then keep the plugin. I've not had to use JSONP in any real capacity, but I would assume that error handling is always nice to have. In the link to the jQuery docs, there is good discussion on this towards the end of the comments
JQuery HowTo has a post specifically about using jQuery, JSONP, and Twitter.
Twitter JSON(P) API URL:
http://twitter.com/status/user_timeline/USERNAME.json?count=10&callback=JSONPcallbackFunction
Here is a code to use with jQuery’s
$.getJSON() function:
http://twitter.com/status/user_timeline/USERNAME.json?count=10&callback=?
We have put ? (question mark) for
callback function name so jQuery could
replace it with a dynamic one that it
has created.
Related
I can not figure out what could be the usefulness for this function:
// Quick function to retrieve the parameters in a format compatible with the ajax request
var getRequestParameters = function(params){
return params;
};
I'm seeing it in a web project, used before every $.ajax() call. Can anybody please enlighten me?
It's an extension point. It serves as a central point to add/remove/customize parameters at some point in the future if the needs of the project change. At present, it doesn't do anything, but apparently it's there on the theory that eventually it may do something.
You've said it's used before every $.ajax call, which suggests you're using jQuery. Which means it's a bit redundant with jQuery's own ajaxSend, but that's not a big deal.
For example, suppose at some point in the future you needed to add a unique identifier to every ajax request that was sent. You'd just have to modify that one function:
var id = 0;
var getRequestParameters = function(params){
params.__uniqueId = ++id; // Or perhaps copy params first
return params;
};
...and you'd have it on all of your ajax requests.
I thought this would be easy b.c. Backbone uses jQuery and has a single access point I thought I could just do this:
Backbone.ajax = function() {
var xhr = Backbone.$.ajax.apply(Backbone.$, arguments);
// xhr.addEventListener("loadend", loadEnd);
return xhr;
};
but for some reason I can not load an event listener to the xhr object as I normally do.
I get an error stating that addEventListener is not a function.
See jqXHR:
The jQuery XMLHttpRequest (jqXHR) object returned by $.ajax() as of jQuery 1.5 is a superset of the browser's native XMLHttpRequest object.
It is not exactly the native XMLHttpRequest, so it might not behave exactly like the native one. There is no guarantee that it exposes methods like addEventListener unless it's documented. If you want to set something globally it's safer to use jquery.ajaxsetup
Will it be a good or a bad practice to override all ajax call in my Web App's JavaScript code with an "invisible" Cache layer?
It'd be something like (pseudo-code):
$.prototype.ajax = function(...) {
if( requested URL can be found in localStorage )
return dataFromLocalStorage; // as Deferred
data = invoke original $.ajax;
localStorage.setItem(key, data);
return data; // as Deferred
}
I can think of one pro: no refactoring is needed for existing ajax calls, and one con: future developers will be unaware of this functionality, as it disguises itself as a regular ajax call.
What do you think? Will it be a good practice or should I avoid it?
No, it is not a good idea to override the default behavior of $.ajax like this. Other code on your page including jQuery plugins might depend on the default behavior.
It is likely at some point you might want to get the freshest version of some data, if $.ajax always caches to localStorage there will be no way to do that. This could also lead to future debugging headaches when someone working on the code (including you) can't figure out why their AJAX calls keep returning stale data.
It would be much better to just implement the caching in a separate function. That way when you see a call to something like ajaxWithCaching it will be obvious that something more that a plain AJAX call is going on.
I wouldn't be overriding the default behavior of anything! If you change the way $.ajax works, what will you do when you're using a lightbox plugin (or literally anything) which relies on the normal functionality of $.ajax?
It would backfire terribly upon you.
Instead, create your own function which performs the caching, and uses $.ajax normally.
var cache={}; // Your Cache
function getArticle(id,callback){ // Your caching ajax-wrapping function (requires a callback)
// Require the arguments
if (arguments.length<2) throw "getArticle requires id and callback arguments";
// If the article is cached, pass it to the callback and return
var cachedArticle = id in cache
? cache[id]
: null;
if (cachedArticle) return callback(cachedArticle,true); // cached=true
// If that article isn't in the cache, perform an ajax call to get it
$.ajax({
url: "article.php",
success: function(data,textStatus,jqXHR){
cache[id] = data; // Cache it!
callback(data,false); // Pass it to the callback // cached=false
}
});
return true; // reporting that we launched an ajax request
}
Then to use it, you've got a pretty robust little function.
var ajaxWasUsed = getArticle(8, function(articleData,wasCached){
console.log("Got Article #8 "+(wasCached?"(from cache)":"(fresh ajax)")+":",articleData);
});
The jQuery documentation lists the following example of using $.getJSON to request JSONP:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?",
function(data) {
$.each(data.items, function(i,item) {
$("<img/>").attr("src", item.media.m).appendTo("#images");
if (i == 3) return false;
});
});
Rather than use this method, which generates a dynamic callback function name because of this parameter:
jsoncallback=?
I want to be able to set that in advance to a hardcoded function name, like this:
jsoncallback=test
This works, in the sense that I run the script and the JSONP that I get back has the JSON object wrapped in a call to test().
However, I can't figure out how to set up the callback function. Shouldn't it be as simple as this?
function test(data) {
console.log(data);
}
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=test");
When I try that, I get back the JSONP which is wrapped in test(), but the function test() that I've defined is never called. Am I missing something?
Thanks for any help!
As defined in the documentation for you to use the following method
jQuery.getJSON(...)
you need to specify callback=? when making a JSONP call. I usually only uses this for response types of "json". For response types of "jsonp", you want to use:
jQuery.get(...)
and specify the type as "jsonp". See this documentation on the subject. But that is also bound by the fact of having to have a callback=?.
What I think you are looking for is this:
jQuery.getScript(...)
Which should execute whatever method you have defined in your callback.
Ah, the "Related" sidebar section saved me here. After I submitted this question, I found a similar one already asked:
using a named function as the callback for $.getJSON in jQuery to satisfy Facebook request signing demands
Duncan's answer from Oct. 15 solved this for me:
window.fixed_callback = function(data){
alert(data.title);
};
$(function() {
$.getScript("http://api.flickr.com/services/feeds/photos_public.gne?tags=cats&tagmode=any&format=json&jsoncallback=fixed_callback", function(data) {
alert('done'); } );
});
I guess the key is using $.getScript instead of $.getJSON. One can still specify an anonymous callback function in the parameters of the $.getScript method, which will be executed after the callback function named in the request URL parameters ("fixed_callback" in this case). Hope this helps someone down the road.
I have an application that uses Ajax.Request and its onSuccess event handler in lots of places.
I need to call a function (that will check the response) before all these onSuccess events fire. I tried using Ajax.Responders.register with onComplete event but it fires after Ajax.Request's onSuccess event. Any suggestions?
similar to Aleksander Krzywinski's answer, but I believe this would prevent you from having to sprinkle the use of "wrap" everywhere, by consolidating it to the onCreate Responder.
Ajax.Responders.register({
onCreate: function(request) {
request.options['onSuccess'] = request.options['onSuccess'].wrap(validateResponse);
}
});
There are several events to chose from. Here is the event chain for Ajax.Request:
onCreate
onUninitialized
onLoading
onLoaded
onInteractive
onXYZ, onSuccess or onFailure
onComplete
onLoading, onLoaded, onInteractive sound interesting, but according to the spec they are not guaranteed to happen. That leaves you with the possibility to hook on to onCreate, which is called just after the request object is build, but before the request is actually made.
This might be a little late, but for the benefit of anyone else wondering about the same problem I will propose this solution:
You can use Prototypes own implementation of aspect-oriented programming to do this. Granted you will have to modify all your onSuccess-parameters, but it can be done with a simple search-and-replace, instead of updating all your callback functions. Here is an example Ajax.Request creation:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful
});
Say you have similar code snippets spread all over your code, and you want to preceed them all with a certain function that validates the response before the actual function is run(or even prevented from running at all). By using Funtion.wrap supplied in Prototype we can do this by extending the code above:
new Ajax.Request('example.html', {
parameters: {action: 'update'},
onSuccess: this.updateSuccessful.wrap(validateResponse)
});
Where 'validateResponse' is a function similar to this:
// If you use the X-JSON-header of the response for JSON, add the third param
function validateResponse(originalFn, transport /*, json */) {
// Validate the transport
if (someConditionMet) {
originalFn(transport /*, json */);
}
}
Thus you have extended your onSuccess-functions in one central place with just a quick search for onSuccess and pasting in 'wrap(validateResponse)'. This also gives you the option of having several wrapper-functions depending on the needs of the particular Ajax-request.
You can run your method before the other code in onSuccess and return false if something is wrong.
Don't know if this is the cleanest solution, but for me it did the trick.
var tmp = Ajax.Request;
Ajax.Request = function(url, args) {
// stuff to do before the request is sent
var c = Object.clone(args);
c.onSuccess = function(transport){
// stuff to do when request is successful, before the callback
args.onSuccess(transport);
}
var a = new tmp(url, c);
return a;
}
Ajax.Request.protoype = new tmp();
Ajax.Request.Events = tmp.Events;
delete tmp;
"General solution" - independent upon JS framework (kind of)
var oldFunc = Ajax.Request.onSuccess;
Ajax.Request.onSuccess = function foo() {
alert('t');
oldFunc.apply(this, arguments);
}
This will "extend" your JS function making it do exactly what it used to do except show an alert box every time before it executes...