One question I see a lot on the Appcelerator Titanium Developer Q&A is how to perform AJAX requests and/or work with APIs, etc. There is a built-in way to do this with the Ti.Network.HTTPClient module that is pretty easy, but it does have a few drawbacks and “gotchas”, like executing the “success” event for ANY returned status code – even 500 errors. Since working with APIs is so common with mobile apps, I made a wrapper function modeled after jQuery’s $.ajax method that I use in all my apps. It shortens the syntax quite a bit and is much more familiar to those who are used to using jQuery.
The usage looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // Geocode input text location app.utils.ajax({ url: 'http://maps.googleapis.com/maps/api/geocode/json?address=' + txtLocation.value +'®ion=us&sensor=true', method: 'get', success: function(xhr) { var data = JSON.parse(xhr.responseText); Ti.API.info(data); if("OK" == data.status) { var res = data.results[0]; if(res) { alert("Location: " + res.geometry.location.lat + ', ' + res.geometry.location.lng); } // Do something with coordinates } else { Ti.UI.createAlertDialog({ title: 'Geocode Error', message: 'Unable to geocode location input' }).show(); } }, error: function(xhr) { Ti.UI.createAlertDialog({ title: 'Geocode Error', message: 'No location matches found. Please try something else.' }).show(); } }); |
And the actual code for the utility function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | /** * Application Utilities and Helper Methods **/ (function(_app) { _app.utils = {}; // AJAX method that mimmicks jQuery's _app.utils.ajax = function(_props) { // Merge with default props var o = _app.combine({ method: 'GET', url: null, data: false, contentType: 'application/json', // Ti API Options async: true, autoEncodeUrl: true, // Callbacks success: null, error: null, beforeSend: null, complete: null }, _props); Ti.API.info("XHR " + o.method + ": \n'" + o.url + "'..."); var xhr = Ti.Network.createHTTPClient({ autoEncodeUrl: o.autoEncodeUrl, async: o.async }); // URL xhr.open(o.method, o.url); // Request header xhr.setRequestHeader('Content-Type', o.contentType); if(o.beforeSend) { o.beforeSend(xhr); } // Errors xhr.setTimeout(10000); xhr.onerror = function() { Ti.API.info('XHR "onerror" ['+this.status+']: '+this.responseText+''); if(null !== o.error) { return o.error(this); } }; // Success xhr.onload = function() { // Log Ti.API.info('XHR "onload" ['+this.status+']: '+this.responseText+''); // Success = 1xx or 2xx (3xx = redirect) if(this.status < 400) { try { if(null !== o.success) { return o.success(this); } } catch(e) { Ti.API.info('XHR success function threw Exception: ' + e + ''); return; } // Error = 4xx or 5xx } else { Ti.API.info('XHR error ['+this.status+']: '+this.responseText+''); if(null !== o.error) { return o.error(this); } } }; // Send if(o.data) { Ti.API.info(o.data); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send(o.data); } else { xhr.send(); } // Completed if(null !== o.complete) { return o.complete(this); } }; // And it does depend on this code below to combine object properties: // Extend an object with the properties from another // (thanks Dojo - http://docs.dojocampus.org/dojo/mixin) var empty = {}; function mixin(/*Object*/ target, /*Object*/ source){ var name, s, i; for(name in source){ s = source[name]; if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){ target[name] = s; } } return target; // Object }; _app.mixin = function(/*Object*/ obj, /*Object...*/ props){ if(!obj){ obj = {}; } for(var i=1, l=arguments.length; i<l; i++){ mixin(obj, arguments[i]); } return obj; // Object }; // Create a new object, combining the properties of the passed objects with the last arguments having // priority over the first ones _app.combine = function(/*Object*/ obj, /*Object...*/ props) { var newObj = {}; for(var i=0, l=arguments.length; i<l; i++){ mixin(newObj, arguments[i]); } return newObj; }; })(app); |
The particular organization of this utilities file assumes that your app structure follows the organization model shown in the Tweetanium example app (using the ‘app’ namespace within a single window context), but is easy to adapt if you are not.



Hi – thanks for this! You seem to be a guru on AJAX so I have a quick question. I am looking for a very simple AJAX function/object to allow me to do the following:
// pseudo code
var respData = AJAX(‘http://path/to/ajax/file.php’, ‘POST’, {param1:’value1′,param2:’value2′} );
Ti.API.Debug(respData.responseData); // JSON Response
var table = Ti.UI.createTableView();
table.data = respData.responseData;
// end pseudo code
Basically an AJAX function wrapper that I can call wherever, whenever, and the application will wait for the response and it will be stored to a variable. This has eluded me for sometime now. Any ideas?
Thank you! Tony
Tony -
That IS possible in Titanium, but you shouldn’t do it because a call like that would have to make a synchronous (blocking) AJAX call to return the response data for the next line of your program to use.
The best solution is to do something like this, with a “success” callback function that executes whenever the remote HTTP response is returned:
$.ajax({
url: ‘http://path/to/ajax/file.php’,
method: ‘POST’,
data: {param1:’value1′,param2:’value2′},
success: function(xhr) {
Ti.API.Debug(respData.responseData); // JSON Response
var table = Ti.UI.createTableView();
table.data = respData.responseData;
}
});