Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Marionette regions needs a preventReRender option #2328

Closed
bgaillard opened this issue Feb 13, 2015 · 6 comments
Closed

Marionette regions needs a preventReRender option #2328

bgaillard opened this issue Feb 13, 2015 · 6 comments

Comments

@bgaillard
Copy link

Hi, I have a use case where I need a region to attach / dettach multiple views without rerendering those views on 2nd, third, fourth, etc... Region.show() calls.

Here is a fiddle to explain what I need : http://jsfiddle.net/g7kzpe2p/3/, and the code of this fiddle here.

<script id="view1-tpl">Hello view 1</script>
<script id="view2-tpl">Hello view 2</script>
<script id="layout-view-template" type="text/html">
    <button id="btn1">Btn1</button>
    <button id="btn2">Btn2</button>
    <div id="my-region"></div>
</script>

<div id="layout-view"></div>
<div id="render-log"></div>
var View1 = Marionette.ItemView.extend({
    template: '#view1-tpl', 
    onRender : function() { 
        $('#render-log').append('<p>view 1 render (called each time show is called)</p>'); 
    }
});
var View2 = Marionette.ItemView.extend({
    template: '#view2-tpl', 
    _rendered : false,

    // We want this to be called only on first show
    onRender : function() { 
        $('#render-log').append('<p>view 2 render (called only the first time show is called)</p>'); 
    },
    render : function() {
        if(!this._rendered) {

            // Code copied from ItemView.render()
            this._ensureViewIsIntact();
            this.triggerMethod('before:render', this);
            this._renderTemplate();
            this.bindUIElements();
            this.triggerMethod('render', this);

            // To not re-render each time 'Region.show' is called 
            this._rendered = true;
        }

        return this;
    }
});
var ParentView = Backbone.Marionette.LayoutView.extend({
    events : {
        'click @ui.btn1' : '_onBtn1Click',
        'click @ui.btn2' : '_onBtn2Click'
    },
    regions : {
        myRegion : '#my-region'
    },
    ui : {
        btn1 : '#btn1',
        btn2 : '#btn2'
    },
    onRender : function() {
        this._view1 = new View1();
        this._view2 = new View2();
        this.myRegion.show(this._view1, { preventDestroy : true });
    }, 
    _onBtn1Click : function(clickEvent) {
        this.myRegion.show(this._view1, { preventDestroy : true });
    },
    _onBtn2Click : function(clickEvent) {
        this.myRegion.show(this._view2, { preventDestroy : true });
    }
});

var parentView = new ParentView({ 
    el : '#layout-view',
    template : '#layout-view-template' 
});
parentView.render();

This samples shows that a Marionette Region is always re-rendering its views even if the preventDestroy option is true.

In our case this is problematic because our View1 and View2 views are huge and have to be cached after a first Region show is called.

After talking to @cmaher on gitter it appears that adding a preventReRender option to the Marionette region could be a good idea to have a behavior similar to View2 in my sample.

The code to show our views would be :

this.myRegion.show(
    this._view1, 
    { 
        preventDestroy : true,
        preventReRender : true
    }
);

Any thoughs about that ?

@cmaher
Copy link
Member

cmaher commented Feb 13, 2015


(edit: see below)

@samccone
Copy link
Member

ugh the region api keeps growing and growing :(

@cmaher
Copy link
Member

cmaher commented Feb 14, 2015

I was originally in favor of adding this option, but I think a more forward-thinking approach would be to refactor Region#show to make parts of it overridable.

show: function (view, options) {
  var shouldDestroyView = this.shouldDestroyView(view, options);
  //...
  if (shouldDestroyView) {
    this.renderView(view, options);
    // ...
  }
  // ...
}

To solve this particular problem, you could override renderView on a special region.

var CachingRegion = Marionette.Region.extend({
  shouldDestroyView(view, options) { return false; },
  renderView(view, options) {
    if (!view.isRendered) { view.render(); }
  }
}

For v3, this also affords us gives us some room to remove a bunch of these ugly options, instead giving users a more flexible API.

@bgaillard
Copy link
Author

Hi @cmaher, I totally agree with you 👍 .

Allowing the developer to control this "prevent re-render" behavior by creating a specialized Marionette Region is much more flexible.

@cmaher cmaher modified the milestone: v2.5 Feb 24, 2015
@paulfalgout
Copy link
Member

solution provided in #2335 added in next

@bgaillard
Copy link
Author

Great, thanks for this, we'll try it on our next Marionette app 😃

@paulfalgout paulfalgout modified the milestone: v2.5 Aug 18, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants