Exception from $ .ajax ({success: function ()}) dungeon
So I have a general scenario where everything depends on AJAX responses and then maybe more AJAX responses.
The result is a multiple and multiple presentation of the (page-specific) code called in the success () callback :
$.ajax({
...
success: function (response) {
// too much $('#something').html() crap goes in here!
}
});
What's the best practice to completely remove this data access code from the presentation code, while maintaining the load sequence?
I've used delayed calls such as $.when().then()
but it still seems sloppy to me. Anything better?
To make the answer even easier, let's say I want to get information about an object Person
and separate all this logic in my scope. Example:
note: this code won't work - I know that
Person.js:
var Person = {
getByID: function(id) {
// ajax call to return a person object (format is arbitrary)
// { id: 12345, name: 'Joe Smith', email: 'joe@smith.com }
}
};
SomePage.html
var myID = 12345; // get ID from wherever
var person = Person.getByID(myID);
$('#person .name').html(person.name);
$('#person .email').html(person.email);
EDIT: My solution
While many of the answers were helpful, I decided to pass in callback functions that separate all the different pieces of logic from each other. Sample code:
JS:
Person = {
get: function(params, callback) {
$.ajax({
url: '/person/get',
data: params,
success: callback
});
}
};
Pages = {
Person: {
render: function(person) {
// in reality I'm using templates, this is for simplicity
$('#person .name').html(person.name);
$('#person .email').html(person.email);
}
}
};
SomePage.html
$('#someElement').someEvent(function() {
var params = {
id: 12345
};
Person.get(params, Pages.Person.render);
}
I should also add that this slide is extremely informative:
http://speakerdeck.com/u/addyosmani/p/large-scale-javascript-application-architecture
source to share
Now I know the correct answer:
There are over 9000 libraries built specifically for this, a common problem known as AMD.
-TerryR
http://speakerdeck.com/u/addyosmani/p/large-scale-javascript-application-architecture
source to share
As SLAX says, what you want to do is not possible in an asynchronous AJAX context.
However, there is nothing against having most of the code in separate objects that do all the nagging work. Then you call these objects and their methods from your success callbacks and pass all the data you need to them. This will ensure that your callbacks only contain minimal code.
source to share
It seems to me that you can introduce the Model-View-Controller design pattern into your application. In its simplest form, it Model
will be responsible for fetching the required data, which is then passed to View
for display; the job Controller
is to process incoming requests, call Model
and shuffle the data in View
the expected format.
As mentioned, there are many lightweight MVC frameworks for JavaScript ; but you have to get the basic concept and run it with jQuery and a templating engine (like Mustache.js ) so you can get some separation between the model (fetching the data and building a common object graph) and the view (rendering the template and adding the rendered HTML to DOM).
Regarding the Person example you suggested, you can use jQuery Deferred so that you Person.getById
can return a Promise, like so:
var Person = {
getByID: function(id) {
var result;
// Return a Promise which we will resolve once the AJAX call completes.
return $.Deferred(function (dfd) {
$.ajax(...)
.done(function (response) {
// Parse the AJAX response into your app model data.
result = {
id: response.id,
name: response.firstName,
email: response.email
};
// Resolve the Promise and supply the Person object.
dfd.resolve(person);
});
}).promise();
}
};
Then yours Controller
can call Model
and then pass the result to View
(you can use templates here).
Person.getByID(myID)
.done(function(person) {
$('#person .name').html(person.name);
$('#person .email').html(person.email);
});
source to share
You can modify your example slightly to make it work asynchronously - still cleaner code than switching to a callback function:
var myID = 12345; // get ID from wherever
Person.getByID(myID, function(person) {
$('#person .name').html(person.name);
$('#person .email').html(person.email);
});
source to share