Search code examples
javascriptbackbone.jsbackbone-viewsbackbone-routing

Start Backbone routing when sub-views are available (backbone.layoutmanager)


I've hit a head-scratcher with a Backbone.js. The example is on jsfiddle here. I believe the issue is here:

App.Layout = new Backbone.Layout({
// Attach the Layout to the main container.
collection: App.chapters,
el: "body",
initialize: function () {},
beforeRender: function () {
    // Add a sub-view for each Chapter
    this.collection.each(function (model) {
        this.insertView(model.get('id'), new App.ChapterView({
            "id": model.get('id')
        }));
    }, this);
},
views: {
    // But if I set the sub-view specifically if works
    // "one": new App.ChapterView({id: 'one' })
}
});

In summary, the router should simply activate or deactivate backbone.layoutmanager sub-views based on the path, e.g., /#chapter/one, /#chapter/two, etc.

If I explicitly set the sub-views in App.Layout (see line 49 in the fiddle), the routing works as expected.

However, if I try to add the views by iterating a collection of models in the beforeRender function (line 40; beforeRender is coming from backbone.layoutmanager), they don't appear to be available when the router tries to find the matching view by ID.

Once the page has render, however, the view can be activated with:

App.router.navigate('/chapter/two',{"trigger": true});

Which seems to indicate that the views are properly being added and should be findable by the router with:

App.Layout.getView(name);

No doubt I'm simply overlooking something, or am about to expose my ignorance of the Backbone library. :)


Solution

  • The issue is that you're navigating and rendering out-of-sync. I've updated your code here: http://jsfiddle.net/6h268r7j/55/

    It works when you use the declarative approach because those are outside of the render flow, essentially statically added. As soon as you use beforeRender/render you are now in an asynchronous render flow and they won't be available in your router callbacks.

    The fix was to simply render the application layout first and then trigger the routing:

    App.Layout.render().then(function() {
      Backbone.history.start();
    });