3
votes

I have two layouts. The first layout is responsible for rendering a sub layout (an soon to be many sub layouts w/ views as my interface is rather complicated).

Here's the first layout:

App.module('Layouts', function(Layouts, App, Backbone, Marionette, $, _) {

    Layouts.Editor = Backbone.Marionette.Layout.extend({

        tagName: 'div',
        className: 'content container-fluid',

        regions: {
            stageContainer: '#stage-container',
        },

        initialize: function() {

            this.template = Backbone.Marionette.TemplateCache.get('editor');

        },

        render: function() {

            var _this = this;

            this.model = new App.Models.App();
            this.model.url = GLOBAL.api.url + '/clients/' + GLOBAL.cid + '/sweepstakes/' + GLOBAL.aid;
            this.model.fetch({ success: function() {

                // show template
                $(_this.el).html(_this.template());

                // show region content
                _this.showStageContainer();

            } });

        },

        showStageContainer: function() {

            var content = new App.Layouts.EditorStageContainer({
                model: this.model
            });
            this.stageContainer.show(content);

            return content;

        }

    });

});

This renders just fine. However, when I attempt to render my sub layout in the showStageContainer function, the render is called but my content is not actually rendered. It's worth to note that I'm calling showStageContainer after fetching a model, as I need to pass the model down to the layout and children views. Here's what the stage container layout looks like:

App.module('Layouts', function(Layouts, App, Backbone, Marionette, $, _) {

    Layouts.EditorStageContainer = Backbone.Marionette.Layout.extend({

        el: '#stage-container',
        tagName: 'div',

        regions: {
            nav: '#nav',
            progress: '#progress',
            stage: '#stage'
        },

        initialize: function() {

            this.template = Backbone.Marionette.TemplateCache.get('editor_stage_container');

        },

        render: function() {

            console.log('render');

            $(this.el).html(this.template());

        },

        onShow: function() {

            // TODO: figure out why the template has to be rendered on show rather
            // than in the render. (render is never being called as onRender is not called)
            $(this.el).html(this.template());

            // show region content
            this.showNav();

        },

        showNav: function() {

            var content = new App.Views.EditorStageNav();
            this.nav.show(content);

        }

    });

});

As you can see, I'm having to manually render the template inside of the onShow function. Generally, this should happen automatically within the render function.

What is most interesting, is that if I wrap a setTimeout around $(this.el).html(this.template()); in the render function, all is well... Which leads me to believe something is not executing in order. Example:

var _this = this;
render: function() {
    setTimeout(function() {
        $(_this.el).html(_this.template());
    }, 1);
}

Any thoughts? At a loss here and could really use some help. Thanks!

2

2 Answers

2
votes

I believe you are running into an issue with timing. The region stageContainer does not exist until after the DOM is rendered. Once the html is rendered, Marionette will point stageContainer to the element #stage-container. As you have found, you can easily access the region in the onRender method because onRender is kicked off after the DOM exists.

There are many work arounds and I find many of them so so. Personally I break this part of the application into smaller pieces by only rendering stageContainer after the parent container has been rendered.

**Edit

I just came across a link for a Marionette.js plugin called BossView. It looks interesting but most of all, it will give you example of both doing what you are asking in Marionette and then how to do it in BossView.

0
votes

Even though Marionette has updated from Layout to LayoutView, this is still a problem. The child views are not attaching because of a timing issue. The LayoutView doesn't have its DOM components attached when you are trying to show its children. In the Marionette 2.4.7, when using LayoutView, the correct solution is to show your children in the onBeforeShow method.

You can find further documentation here: http://marionettejs.com/docs/v2.4.7/marionette.layoutview.html#efficient-nested-view-structures