Nesting Layout

I am running in circles, it would seem that something is missing in my current application using backbone.js. The problem is that I have an AppView wizard that initializes various subheadings (graph, information table, etc.) for the page. My desire is to be able to change the page layout depending on the flag of the parameter passing during navigation.

What I came across is the case when subviews objects that refer to dom elements that will be placed after the template is rendered do not have access to these elements during the main AppView initialization process. So the main question is how to ensure that the proper dom elements are created for each event binding process in order to properly configure it?

Using the following code, if I have an event related to a model change in my LayoutView, the layout is rendered, but subsequent views are not displayed properly. Some thing I came across was setting all the .el values ​​as a static element in the html structure. This leads to rendering, although it does seem to tear itself away from the good reach provided by using .el.

Code example:

//Wrapped in jquery.ready() function... //View Code GraphView = Backbone.View.extend({ // init, model bind, template, supporting functions}); TableView = Backbone.View.extend({ // init, model bind, template, supporting functions}); LayoutView = Backbone.view.extend({ initialize: function() { this.model.bind('change:version', this.render, this); }, templateA = _.template($('#main-template').html()), templateB = _.template($('#secondary-template').html()), render: function() { if ('main' == this.model.get('version')) { this.el.html(this.templateA()); } else { this.el.html(this.templateB()); } }, }); MainView = Backbone.View.extend({ initialize: function() { this.layoutView = new LayoutView({ el: $('#layoutContainer'), model: layout, }); this.graphView = new GraphView({ el: $('.graphContainer'), model: graph }); this.tableView = new TableView({ el: $('#dataTableContainer', model: dataSet }); }, }); // Model Code Layout = Backbone.Model.extend({ defaults: { version: 'main', }, }); Graph = Backbone.Model.extend({}); dataSet = Backbone.Model.extend({}); // Router code... // Fire off the app AppView = new MainView($('#appContainer')); // Create route, start up backbone history 

HTML: For simplicity, I just rebuilt the divs between the two layouts.

 <html> <head> <!-- include above js and backbone dependancies --> </head> <body> <script type="text/template" id="main-template"> <div class="graphContainer"></div> <div class="dataTableContainer"></div> <div>Some other stuff to identify that it the main template</div> </script> <script type="text/template" id="secondary-template"> <div class="dataTableContainer"></div> <div>Rock on layout version two!</div> <div class="graphContainer"></div> </script> <div id="appContainer"> <div id="nav"> <a href="#layout/main">See Layout One</a> <a href="#layout/secondary">See Layout Two</a> </div> <div id="layoutContainer"></div> </div> </body> </html> 

Appreciate the understanding / input that any of you guys can have. Thanks!

+7
source share
1 answer

You have a lot of missing code in your example, but if I understand correctly, the problem is that you are trying to display HTML-dependent representations using the LayoutView , but the layout is selected asynchronously when the model is updated, so the LayoutView is not yet displayed.

There are (like almost all base-related) multiple approaches to this, but in general:

  • If I have views that depend on the displayed "parent" view, I take responsibility for creating and rendering these views in the parent view - in this case, LayoutView , not MainView ,

  • If the parent is processed asynchronously, it needs to perform this instantiation in a callback - here, the LayoutView.render() method.

Therefore, I could structure the code as follows:

 LayoutView = Backbone.view.extend({ initialize: function() { this.model.bind('change:version', this.render, this); // you probably want to call this.model.fetch() here, right? }, templateA: _.template($('#main-template').html()), templateB: _.template($('#secondary-template').html()), render: function() { if ('main' == this.model.get('version')) { this.el.html(this.templateA()); } else { this.el.html(this.templateB()); } // now instantiate and render subviews this.graphView = new GraphView({ // note that I'm using this.$ to help scope the element el: this.$('.graphContainer'), model: graph }); this.tableView = new TableView({ el: this.$('.dataTableContainer'), model: dataSet }); // you probably need to call graphView.render() // and tableView.render(), unless you do that // in their initialize() functions }, }); 

Note that you do not need to pass el when creating the instance — you can create substrates in initialize() and set the el property to subview.render() . You can even create an instance of MainView.initialize() , as it is now, and pass the LayoutView for rendering asynchronously. But I think the above approach is probably cleaner.

+8
source

All Articles