I would like something like the following behavior:
- Call get() on a collection.
- Fetch the model only if it has not yet been fetched.
- Return the model or fire an event signifying that it is ready.
Is there a standard way to do this in backbone?
I would like something like the following behavior:
Is there a standard way to do this in backbone?
There is no standard method in backbone to do this and requires you to modify the model and collection. I know that you haven't mentioned jQuery in the question, I just use it in the example I am providing for its deferred object capabilities and can be swapped out for any deferred object library.
The problem with this approach is that the collections get()
method will have to return a deferred object for you to work with rather than the model. This is an unfortunate by product of building an asynchronous operation into the collections get()
method.
For the model we will need a way of tracking whether it is fetched or not. Using a timestamp for this can potentially allow you to 'timeout' the model and re fetch it if it is has expired. To do this we override the fetch method on the model.
Warning: this code is untested but provides a way to approach this
var Model = Backbone.Model.extend({
/**
* Last fetched public variable
* @var {Object} Date || null
*/
lastFetched : null,
/**
* Custom fetch always adds a timestamp to the model
* when it was last fetched from the server. This
* allows us to prevent unnecessary calls to the backend
* when a model is still fresh.
* @return {Object} jQuery deferred
*/
fetch : function() {
if ( _.isNull(this.lastFetched) ) {
this.lastFetched = new Date().getTime();
return Backbone.Model.prototype.fetch.apply(this, arguments);
} else {
return new $.Deferred().resolve();
}
}
});
The collection needs to have it's get method overridden to include the model fetch routine. Instead of returning a model, the get method will return a deferred object that can be used to chain callbacks for when the model is ready.
var Collection = Backbone.Collection.extend({
model : Model,
/**
* Custom get retrieves a model if it exists and returns the result
* of the models custom fetch method
* @param {Integer} id | cid
* @return {Object}
*/
get : function(id) {
var model = Backbone.Collection.prototype.get.apply(this, args);
if ( ! model )
model = new this.model({id : id});
return {
deferred : model.fetch(),
model : model
};
}
});
Usage
var collection = new Collection(),
result = collection.get(1);
result.deferred.done(function() {
console.log(result.model);
});
This would allow you to use the workflow you provided in the question.