Backbone-relational icon indicating copy to clipboard operation
Backbone-relational copied to clipboard

Fetch Children of a Model

Open neilpoulin opened this issue 11 years ago • 0 comments

I don't know if there is currently a way to fetch all of the "children" of a relational model or not, but a few months ago I found a need to do this so created a little extension for Backbone Relational. I wanted to do this in order to not have to fetch the entire collection at once, but rather fetch a subset when needed.

Basically, I tweaked the .fetch() method:

Backbone.RelationalModel.prototype.fetchChildren = function( key, options, update ){
    options || ( options = {} );
    var setUrl = null,
        requests = [],
        rel = this.getRelation( key ),
        keyContents = rel && rel.keyContents;

        //don't bother getting wineIds if we are going to force an update
        var wineIds = null;
        if (!update){
            wineIds = _.pluck(Backbone.Relational.store.getCollection(rel.relatedModel), rel.keySource);
        }

    if ( update || _.contains(wineIds, keyContents) ) {

        // 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( this );
        }

        // 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').
        if ( setUrl && setUrl !== rel.related.url() ) {
            var opts = _.defaults(
                {
                    error: function(collection, xhr, options) {
                        console.log("error: " + xhr);
                    },
                    url: setUrl
                },
                options,
                { add: true }
            );

            requests = [ rel.related.fetch( opts ) ];
        }
        else {
            console.log("error: setUrl && setUrl !== rel.related.url() == FALSE");
        }
    }
    return requests;
};

This code depends on the model having URL that is a function that takes a model as an argument, and returns a URL with that model's ID in the URL, with a backend that knows how to sort and return the applicable models. For example:

var TastingNoteCollection = Backbone.Collection.extend({
    url: function(models){
        var url = "/rest/WineCellar/tastingNotes";
        if (models != undefined){
            var wineId = models.get("wineId");
            if (wineId != undefined && wineId != null){
                url = "/rest/WineCellar/wines/" + wineId + "/tastingNotes";
            }
        }       
        return url;
    },
    model: TastingNoteModel,
    initialize: function(){ }
});

I'm not sure if there is already a more preferred way to do this, but the above worked for me.

neilpoulin avatar Mar 12 '13 19:03 neilpoulin