Backbone: Collection and Model fetching/saving etc status

Created on 24 Jan 2013  ·  9Comments  ·  Source: jashkenas/backbone

To be able to know if the collection was populated, or is beeing populated, there might be something like


collection.fetch();
collection.isFetching()===true; // display loading 

(collection.isFetching()===false; && collection.length) // display no records found

Real usecase (simplification, the objects are passed deep in the system):

javascript() // somewhere in the code collection = new MyCollection(); // somewhere else in the code view = new MyView({collection:collection}); // and somewhere else in the code collection.fetch();

the view listens for collection signals, and displays either loading/no records/records list

for model, there could be even save/delete/update thing

question

Most helpful comment

@braddunbar , @caseywebdev the idea is to decouple those two parts. (view should be able to get collection in any state, not just _'clean'_ collection)
I am aware that I can do this using signals, but in many cases this can not be done this way. The order of actions matters

I agree that view should listen for 'request' and 'sync' signals, but the request signal might be sent before the collection is passed to the view, therefore view has no idea that the collection is fetching.

Consider an example, where your view gets an arbitrary collection. This collection can be either _fetched_ , _fetching_ or _unfetched_ when it is passed to the view (tl:dr see example 3).

Example 1 (where signals will work)

collection = new Backbone.Collection();
view = new Backbone.View({collection:collection});
view.render();
// what should the view render ?
collection.fetch() // now sync signal gets sent

Example 2 (view gets already fetched, but emty collection, signals will also work)

collection = new Backbone.Collection();
collection.fetch();  // now sync signal gets sent
// after fetch completes
// view won't be able to receive 'sync' signal
view = new Backbone.View({collection:collection}); 
view.render(); 
// at this point collection.length == 0. 
// I guess the view can listen to 'sync' and then render 'empty' 

Example 3 (most important, signals will not work)

collection = new Backbone.Collection();
collection.fetch();  // now 'request' signal gets sent, but it is pending
// view won't be able to receive 'request' signal
view = new Backbone.View({collection:collection}); // at this point collection.length == 0
// view did not receive the 'request' signal, therefore has no idea that the collection is fetching
// and the view should render  'loading'
// after 2 seconds, 'sync' signal gets sent

View should not assume that if collection.length == 0 then it is unfetched.
View should also not assume that it is given collection in unfetched state (or in any other state).

If we want to decouple collection from view, then view has to be able to know what is the collection state.

the same thing goes for the Model

All 9 comments

Hi @g00fy-! I would recommend that you use the "request" and "sync" events for this purpose. What do you think?

collection.on('request', function() {
  // loading...
}).on('sync', function() {
  // display records or "none found"
});
collection.fetch();

_Edit - I meant the "request" event, not "fetch"._

I think that's a swell idea @braddunbar!

Thanks @caseywebdev! :)

Let us know if that doesn't work for you @g00fy-.

@braddunbar , @caseywebdev the idea is to decouple those two parts. (view should be able to get collection in any state, not just _'clean'_ collection)
I am aware that I can do this using signals, but in many cases this can not be done this way. The order of actions matters

I agree that view should listen for 'request' and 'sync' signals, but the request signal might be sent before the collection is passed to the view, therefore view has no idea that the collection is fetching.

Consider an example, where your view gets an arbitrary collection. This collection can be either _fetched_ , _fetching_ or _unfetched_ when it is passed to the view (tl:dr see example 3).

Example 1 (where signals will work)

collection = new Backbone.Collection();
view = new Backbone.View({collection:collection});
view.render();
// what should the view render ?
collection.fetch() // now sync signal gets sent

Example 2 (view gets already fetched, but emty collection, signals will also work)

collection = new Backbone.Collection();
collection.fetch();  // now sync signal gets sent
// after fetch completes
// view won't be able to receive 'sync' signal
view = new Backbone.View({collection:collection}); 
view.render(); 
// at this point collection.length == 0. 
// I guess the view can listen to 'sync' and then render 'empty' 

Example 3 (most important, signals will not work)

collection = new Backbone.Collection();
collection.fetch();  // now 'request' signal gets sent, but it is pending
// view won't be able to receive 'request' signal
view = new Backbone.View({collection:collection}); // at this point collection.length == 0
// view did not receive the 'request' signal, therefore has no idea that the collection is fetching
// and the view should render  'loading'
// after 2 seconds, 'sync' signal gets sent

View should not assume that if collection.length == 0 then it is unfetched.
View should also not assume that it is given collection in unfetched state (or in any other state).

If we want to decouple collection from view, then view has to be able to know what is the collection state.

the same thing goes for the Model

@g00fy If you can't just fetch the collection after sending it to the view, set a fetching flag or such on your collection.

collection.on({
  request: function () { this.fetching = true; },
  'sync error': function () { this.fetching = false; }
});

Now your view can access this.collection.fetching and also wait for this.collection to fire the sync or error event.

@caseywebdev
The request might be aborted, or two requests might get sent at once.

// example 1
collection.fetch().abort()  // and collection.fetching == true
// example 2 - don't care about abort();
collection.fetch();
collection.fetch();
// 1st request compleated
!!collection.fetching == false // but the 2nd request is pending - should it be loading or not?
// 2nd request completed

The solution to this would be to connect do deferred xhr

collection.on({
    'request':function(model,xhr){ 
        this.fetching = true; 
        xhr.fail(function(){this.fetching = false }.bind(this));
    },
    'sync error': function () { this.fetching = false; }
});

That is why, I think this might be handy to have this built in, but on other hand, if every enhancement like this got through, BB code-base would be bloated.

if every enhancement like this got through, BB code-base would be bloated.

I think it's less about bloating than it is about approach. There are many different ways you might want to handle concurrent requests. Backbone provides you with the primitives that you need to handle them and then gets out of the way.

@g00fy- you might want to take a look into writing a finite-state machine for your purposes. Chaplin.js has one that should be pretty easy to replicate, and there's also ifandelse/machina.js (see the blog post) which may suit your specific use case.

Was this page helpful?
0 / 5 - 0 ratings