Last Updated: February 25, 2016
· cpjolicoeur

Backbone.Model Gotcha With options.url

There is a (maybe) peculiar gotcha to be aware of with Backbone.Model and certain attributes passed in as options vs attributes.

tl;dr Passing a url parameter to a collection.fetch() call will set that url on the Backbone.Model directly

UPDATE: The particular "bug" described in this post has been removed in the current master branch of the project, but is not part of the official release as of the time of this writing.

Here is an example that has bitten me twice now. After that, we'll finish with a better solution.

In a Marionette.CollectionView I was trying to do a paginated load of the view's Backbone.Collection instance. The following code has been modified for the sake of example:

if @gallery.get('page') < @gallery.get("total_pages")
    nextPageUrl = "#{@gallery.url()}?page=#{@gallery.get("next_page")&per=#{@gallery.get("per_page")}"
    @collection.fetch(url: nextPageUrl,remove: false).done( => ...)

Options passed in to most Backbone methods will get passed all the way through to any subsequent xhr and callback methods intact (usually via a _.clone). If options.url is provided, that same url will be in the options hash that is passed to the instantiation of the new Backbone.Model call as part of the reset or set command in the fetch() success callback.

Normally this doesn't cause any unintended side effects as it's the first attrs hash parameter that gets set on the model, and not the options hash. However, Backbone.Model has three very special modelOptions that will be attached directly to the model if provided:

var modelOptions = ['url', 'urlRoot', 'collection'];

And inside the Backbone.Model function declaration these modelOptions are used via:

_.extend(this, _.pick(options, modelOptions));

So, using the example code, each model returned from the fetch with have its url attribute set to whatever the calculated value of nextPageUrl was in the fetch call. It doesn't matter what urlRoot or url functions/attributes you declared in those models, the url is now what was in options.url.

Going back to my original bad implementation, the correct way to do this is to create a url method in the collection itself that creates the dynamic url, and then you can just call @collection.fetch(remove: false) without the need for a url parameter.

1 Response
Add your response

For anyone interested, here is the commit where the modelOptions were removed from master:

Currently, only the "options.collections" value is set directly, "url and urlRoot" have been removed.

over 1 year ago ·