问题
I think I don’t quite get the idea behind the proper usage of Backbone routers. Here’s what I’ve got:
I have some data that I fetch from the server when the page loads and then pack it into models and collections. The number of those models and collections is indefinite. I want to use the router to be able to render the certain collection’s view directly from the start.
The problem is: Backbone router starts up early, and since I ask it to access a certain view and trigger its render
action, it cannot do that, because those views are not yet created. That means I actually have to make my routes start up after the fetch is complete.
I don’t know if this is a proper way to do it, but the only idea I came up with is to:
- Wrap the routes definition and the
Backbone.history.start();
bit into a separate top-level-accesible function (i.e. prepare to call it manually later on). - Run that function as the
success
callback for my collections’sfetch()
- The number of those collections is unknown, also I have no way to find out when all of them have been fetched, and I don’t want to start the routes more than once. So I make use of
_.defer()
and_.once()
.
This works, but it sure looks very weird:
Routers:
window.startRoutes = _.once(function() {
var AccountPage = Backbone.Router.extend({
routes: {
'set/:id': 'renderSet',
},
renderSet: function(setId) {
/** … **/
// Call the rendering method on the respective CardView
CardsViews[setId].render();
}
});
var AccountPageRouter = new AccountPage;
Backbone.history.start();
});
Collection:
window.CardsCollection = Backbone.Collection.extend({
model: Card,
initialize: function(params) {
/** … **/
// Get the initial data
this.fetch({success: function() {
_.defer(startRoutes);
}});
},
});
So my question is… am I doing it right? Or is there a better way to do this (must be)?
回答1:
You can define your router ahead of time; it won't do anything until you call Backbone.History.start().
You can bind the "reset" event on your collection to start history like this:
my_collection.bind("reset", _.once(Backbone.History.start, Backbone.History))
Then the router will start doing stuff when your collection is fully loaded. I'm not sure if this is exactly what you're looking for (since you mentioned having a variable number of collections).
I have a similar situation, except that I know in advance which collections I want to have loaded before I start routing. I added a startAfter method to my Router, like so:
window.Workspace = new (Backbone.Router.extend({
. . .
startAfter: function(collections) {
// Start history when required collections are loaded
var start = _.after(collections.length, _.once(function(){
Backbone.history.start()
}))
_.each(collections, function(collection) {
collection.bind('reset', start, Backbone.history)
});
}
}));
and then after I've setup my collections
Workspace.startAfter([collection_a, collection_b, ...])
This could be adapted to work with standalone models too, although I think you'd need to bind to something other than the 'reset' event.
I'm glad I read your example code, the use of _.once and _.defer pointed me in the right direction.
回答2:
I'm just checking in my .render()
method that all required fields are filled, before using it. If it's not filled yet - i'm rendering an 'Loading...' widget.
And all my views are subscribed to model changes, by this.model.bind('change', this.render, this);
, so just after model will be loaded, render()
will be called again.
来源:https://stackoverflow.com/questions/7822542/backbone-routers-wait-for-data-to-be-fetched-first