Skip to content

Instantly share code, notes, and snippets.

@grumpit
Created July 29, 2012 02:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grumpit/3195852 to your computer and use it in GitHub Desktop.
Save grumpit/3195852 to your computer and use it in GitHub Desktop.
Hack to allow Backbone.RelationalModel to fetchRelated() an associated collection as a whole in a single request
// Note: this means that on the collection that will be fetched, a truthy-returning fetchAsCollection() function
// must be present
fetchRelated: function( key, options, update ) {
options || ( options = {} );
var setUrl,
requests = [],
rel = this.getRelation( key ),
/*
If you add something like this pseudoCode, it will handle cases where we want to fetch a collection
as a whole, in a single request, provided there is a truthy-returning fetchAsCollection() fn on the collection
model we're wanting to fetch.
*/
keyContents = rel && rel.keyContents,
toFetch = keyContents && _.select( _.isArray( keyContents ) ? keyContents : [ keyContents ], function( item ) {
var id = Backbone.Relational.store.resolveIdForItem( rel.relatedModel, item );
return id && ( update || !Backbone.Relational.store.find( rel.relatedModel, id ) );
}, this );
// SNIP
if ( toFetch && toFetch.length ) {
// Create a model for each entry in 'keyContents' that is to be fetched
var models = _.map( toFetch, function( item ) {
var model;
if ( _.isObject( item ) ) {
model = rel.relatedModel.build( item );
}
else {
var attrs = {};
attrs[ rel.relatedModel.prototype.idAttribute ] = item;
model = rel.relatedModel.build( attrs );
}
return model;
}, this );
// Try if the 'collection' can provide a url to fetch a set of models in one request.
if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
setUrl = rel.related.url( models );
}
// An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
// To make sure it can, test if the url we got by supplying a list of models to fetch is different from
// the one supplied for the default fetch action (without args to 'url').
/*
Added rel.related.fetchAsCollection() === true ||
*/
if ( setUrl && (rel.related.fetchAsCollection() === true || setUrl !== rel.related.url() ) ) {
var opts = _.defaults(
{
error: function() {
var args = arguments;
_.each( models, function( model ) {
model.trigger( 'destroy', model, model.collection, options );
options.error && options.error.apply( model, args );
});
},
url: setUrl
},
options,
{ add: true }
);
// /*
// Conditional added here, so we use 'reset' instead of add,
// and get rid of the add altogether, then return the single
// request as our results
// */
if ( rel.related.fetchAsCollection() === true ) {
delete opts.add;
// use reset instead of add since we are working with a collection
opts.reset = true;
requests = rel.related.fetch( opts );
}
else {
requests = [ rel.related.fetch( opts ) ];
}
}
else {
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment