Backbone fetch collection lifecycle, set property from response - javascript

Question:
What is the lifecycle of the standard fetch method for a collection in Backbone? i.e. what events/methods are fired and in what order?
Context:
The JSON response I receive from the server for my collection has an array of models and a property:
{
results: [model1, model2],
aProperty: "example"
}
I would like to read this property from the JSON response and set it as a property on the Collection. I am currently overriding the parse function:
parse: function(response, options) {
this.aProperty = response.aProperty;
return response.results;
}
This feels like the wrong place to set properties in the collection - the parse function has a specific job and happens before the model array has been verified.
I have also tried:
initialize: function() {
this.on('sync', function(collection, resp) {
collection.aProperty = resp.aProperty;
});
}
However, 'sync' is called after the success callback for a fetch (I need to set the properties as part of fetch, before the success callback).

After reading the source code some, I think what you want to do is capture it on the request event. It is triggered on the model in Backbone.sync:
model.trigger('request', model, xhr, options);
Here you can override the callback sent as part of the request, wrapping it with the change you want to make. I didn't test this, but maybe this can give you an idea:
this.on('request', function(model, xhr, options) {
var success;
success = options.success;
options.success = function(resp) {
model.aProperty = resp.aProperty;
success();
}
});
Checkout out the annotated backbone source documentation. In particular the bit on Backbone.sync will help.

Related

'error' event fires on my Backbone model no matter the http status code

I am trying to create a base Backbone model that handles all my http errors. Here is the code for the model:
define(function(require) {
'use strict';
var Backbone = require('backbone');
var BaseModel = Backbone.Model.extend({
initialize: function(attributes, options) {
options || (options = {});
this.bind("error", this.defaultErrorHandler);
this.init && this.init(attributes, options);
},
defaultErrorHandler: function(model, error) {
if(error.status === 401 || error.status === 403 || error.status === 500) {
console.log(error.status);
}
}
});
return BaseModel;
});
Apparently the error event fires no matter what happens with the http call. Even if I get a 200 back from the server I still hit my defaultErrorHandler. Could someone please explain what the error event that I have attached to really does? Also, is there some kind of event that I can subscribe to that will only give me true errors?
Backbone's save method will fail if: it does not get a 200 response from the server; the model has a method named validate that returns false. Although the docs don't say this explicitly, if you're saving a new record, I think it expects to get back a hash of attributes including an "id" attribute, which should be an integer. If it's missing, Backbone may assume that the record could not be saved server-side.

Edit Backbone Ajax Success Method to Add in Custom Error Handling

I have my own custom error code inside of a backbone ajax success method in case the server returns an error. The problem is that this code is repeated throughout my app and I wanted to edit the success function in one place so I don't have to constantly repeat this error handler in every ajax success. I want to edit the success function to include this error check wrapper. Do you know how to do that?
Here is an example of my success method in one of my views:
"success" : function success(model, data)
{
if(data['error'] !== undefined && data['error'].length === 0)
{
message('error', 'Whoops! System Error. Please refresh your page.');
}
else if(data['error'] !== undefined)
{
message('error', data['error']);
}
else
{
//add templates and do stuff here
}
},
Ideally I'd like to set that in a config somewhere and then I'd just be able to use:
"success" : function success(model, data)
{
// add templates and do stuff here
}
Is this possible? I tried using ajaxSetup but that didn't seem to work for me.
UPDATED CODE STILL NOT WORKING:
That does get me a little further along but the error handler isn't functioning as a wrapper. The data is not being passed into my ajax calls. In fact, my success methods on my ajax calls aren't running at all anymore. I tried console.log("some text") in my ajax calls but nothing is being output. Do you know what is wrong with this?
// Save the original Sync method
defaultSync = Backbone.sync;
//Over ride Backbone async
Backbone.sync = function(method,
model,
options)
{
success = options.success
options.success = function(data)
{
if(data['error'] !== undefined && data['error'].length === 0)
{
message('error', 'Whoops! System Error. Please refresh your page.');
}
else if(data['error'] !== undefined)
{
message('error', data['error']);
}
else
{
success(model,
data);
}
}
return defaultSync(method,
model,
options)
}
There are two ways to solve this:
Inheriting Backbone Model
You could create your own custom model which inherits from Backbone Model. In it you could override the save method. Read Backbone docs on how to extend their model
In your custom save method, you will call the save method of super, check the responseText, if it's success then you'll call the success callback. (please do read backbone docs on how to call a method of your parent model in Javascript)
Override Backbone.Sync
Backbone has a Sync module which basically by default makes all ajax requests, parses the response and then calls the success/error callbacks you specified when calling save on your model. It's pretty simple. Take a look at this doc . Again you could override this, do exactly what Backbone is doing by default but only call the success/error callbacks based on responseText you received.
UPDATE: Sample Code (warning code not tested)
//Over ride Backbone async
defaultSync = Backbone.Sync // Save the original Sync method. We'll be needing that.
Backbone.Sync = function(method, model, options) {
success = options.success
error = options.error
options.success = function(model, data, options) {
if (/% all your custom checks are true */) {
success (model, data, options);
}
else {
error(model,data,options);
}
}
return defaultSync(method, model, options);
}
Please make sure with this strategy the Sync method will be overriden for ALL your Backbone sync. If you don't want that then use Model#save override.
Take a look at this code where I am overriding Backbone.Sync to make it work with Parse.com API.

Call a function everytime and AJAX request is processed and returned from the server

I have a application where there are numerous number of ajax calls to the server.
Now I want to audit the response that comes from the server (This requirement poped up after the ajax code was laid).
So I have a function that would audit the response data, only problem is how can I get the data to be sent to the function which now sits separately.
I don't want to do the laborious work of adding the line of code for calling the function in each ajax call.
Is there easier and general way out. Somehow I could detect when a response come back and then process the response.
Using both traditional javascript method as well as jquery ajax calls in the system. (The app has been getting changes from a long time and has changed hands a lot so the new things get added and the older ones never get removed)
Wrap your ajax calls with a helper function and use it throughout your code.
An (untested) example:
MyApp = MyApp || {
logRequest: function _logRequest(settings, response) {
// Log your response
},
ajax: function _ajax (settings) {
var that = this;
// Log attempt request here?
// Example of logging the success callback (do similar for error or complete)
if (settings.success) {
// A success handler is already specified
settings.success = function (data) {
that.logRequest(settings, data); // Log the response
settings.success(data); // Call the original complete handler
};
} else {
// No success handler is specified
settings.success = function (data) {
that.logRequest(settings, data);
};
}
return jQuery.ajax(settings);
}
};
I favour this mechanism for lots situations where I want to reduce boilerplate. I only have to modify the state of the MyApp object which is my own (named appropriately for the application), so it is sort of an interface that allows you to intercept function calls without modifying other global objects. You can also swap this functionality out with something else very easily without having to update your references everywhere, which could be useful in a lot of other situations as well.
Using .ajaxComplete() should be enough to catch the onComplete event for all AJAX requests made through jQuery. Isn´t that what you´re asking for?
$('.ajaxRequest').click(function(event) {
event.preventDefault();
$.getJSON(
'/echo/json/',
this.id,
function(data, textStatus, jqXHR) {
console.log(data, textStatus, jqXHR);
}
);
});
// Listen to all ajax requests
$("#log").ajaxComplete(function(event, request, settings) {
console.log(event, request, settings);
});​
View demo.

Declare a javascript object. Then set properties with jQuery and Ajax

I can't access the attribute of an instantiated class. The attribute is set using an AJAX call.
I am trying to define the class "CurrentUser", and then set the attribute "userId" using AJAX.
Here I define the class CurrentUser, and give it the attribute userID:
function CurrentUser() {
// Do an ajax call to the server and get session data.
$.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
this.userId = data.userId;
console.log(data.userId); // This will correctly output "1".
}, "JSON");
}
Here I instantiate a CurrentUser named billybob. Notice how I can't output billybob's attribute:
// Instantiate the user.
var billybob = new CurrentUser();
console.log(billybob.userId); // This will incorrectly ouput "undefined".
I've checked the common errors with AJAX:
The AJAX call returns the data correctly as a JSON object. I can read the correct object in Firebug / Network console. The AJAX call also has a status of "200" and "OK".
I can log the AJAX call's data correctly, as seen in the first part of my code where I log data.userId.
Maybe this clears it out:
In your original code:
function CurrentUser() {
// Do an ajax call to the server and get session data.
$.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
this.userId = data.userId;
console.log(data.userId); // This will correctly output "1".
}, "JSON");
}
You are creating an anonymous function on the fly, that will be later called by jQuery's internals with this
set to an ajax object. So this will be the ajax object inside the anonymous function, not billybob. So when
you do this.userId = ... this means the ajax object which doesn't have a userid property.
jQuery will have no idea where you got your callback function from, so it cannot set the this automagically
for you.
What you must do is to save the billybob (or any CurrentUser instance) reference and use it in the callback like so:
function CurrentUser() {
var self = this;
$.get("../../build/ajaxes/account/get_user_object_data.php", function(data) {
self.userId = data.userId; //self refers to what this refered to earlier. I.E. billybob.
console.log(data.userId, self.userid); // This will correctly output "1".
}, "JSON");
}
Also note that:
var billybob = new CurrentUser();
console.log(billybob.userId);
By the time you call console.log (I.E. instantly after creating billybob), the ajax request hasn't been completed yet so it is undefined.
In constructor, consider doing
var self = this;
and in function
self.userId = data.userId;
this inside function will be different than outside. Although, I don't know JQuery. May be it's supposed to set up closure automatically.

How might I populate an object property using a JSON-encoded server response?

How can i turn this:
<? echo json_encode($myArrays); ?>
...into this:
_rowData: [
{ name: "Most Recent", view: "recentView" },
{ name: "Most Popular", view: "popularView" },
{ name: "Staff Picks", view: "staffView" }
],
My script returns that ^, but i dont know how to put the data into the string, _rowData ?
P.S. I am using Dashcode, trying to dynamically load items into a List Controller
So far, i have this:
var recentListControllerXHR = $.ajax("http://tarnfeldweb.com/applewebapps/ajax/recentApps.php", function(data){
return(JSON.stringify(data));
}, 'text');
rowData: recentListControllerXHR,
Ok - your problem appears to be a misunderstanding of how asynchronous APIs work. $.ajax() makes a request, and at some point in the future calls the provided callback with the response.
Assuming you have a name for the object you're populating, you can fill in the desired property using something like this:
var someObject = {
...
rowData: null,
...
};
// at this point, someObject is incomplete...
$.getJSON("http://tarnfeldweb.com/applewebapps/ajax/recentApps.php",
function(data)
{
// called whenever the server gets around to sending back the data
someObject.rowData = data;
// at this point, someObject is complete.
});
Note that I'm using jQuery's built-in support for JSON here. You can use the json.org library instead if you wish, but getJSON() is rather convenient if you don't have any unusual needs when parsing the data.
Try this:
var rowData;
$.getJSON("http://tarnfeldweb.com/applewebapps/ajax/recentApps.php", function(data) {
rowData = data;
});
But note that rowData is not available until the callback function (see second parameter of getJSON call) has been called.
The question's essentially already been answered using another method, but, if you're interested in using the $.ajax method as opposed to the $.getJSON method, this is how you would do that:
var rowData;
$.ajax({
url: "http://tarnfeldweb.com/applewebapps/ajax/recentApps.php",
type: 'get',
dataType: 'json' // could also be 'jsonp' if the call is going to another site...
success: function(data){
rowData = data;
}
});
Just another option, that's all...
Looks like you don't understand how $.ajax works (looks like nobody do).
$.ajax is an asynchronous function, which means it does not return anything. It only prepares a function to be invoked later, when the data has been received.
Your code should look more like that:
var obj = {
...
_rowData: [],
...
};
$.getJSON("http://tarnfeldweb.com/applewebapps/ajax/recentApps.php", function(data)
{
// Now data contains your row data,
// you can either fill the global object
obj._rowData = data;
doSomethingWith(obj);
// or use data directly
doSomethingWith({
...
_rowData: data
...
});
});
// Note that code below will be executed before the AJAX data is received
Your function will return the data on a successful response.
You have declared the callback function:
function(data){
return(JSON.stringify(data));
}
So, 'data' will contain any data that was returned from the request, if you're declaring your content type as text/json (or application/json - I don't remember off the top of my head) and
rendering your JSON as text in the response you should be good.
What you probably want to do is have your function declare the variable as rowData and go from there, so do something like:
function(rowData){
// do something with the rowdata
}
I doubt you need the stringify method unless you're trying to write the data out as text.

Categories