Search code examples
javascriptbackbone.js

How to be sure I always call remove on a view?


I have a region on the page, let's say given by:

<div id="region1"></div>
<div id="region2"></div>

I have several views in the app. At any one time, any one of them may be rendered into #region1. Then, another one is rendered.

I don't want to just call $('#region1').html('') to get rid of the html from the last view in the app region. I want to call .remove() on the being discarded view. But I don't know which one it is.

What is the best pattern for dealing with this situation? Should I be tracking the "active" view in something in my application code and calling remove on that?

I.e. my application has something like:

//update this every time a view is rendered into region1
app.regions.region1.currentView = viewA ;

then when I render another view into region1, I first call:

app.regions.regions1.currentView.remove() 

Solution

  • One way to share a div and keep track of the view being rendered is to manage the app layout with a view.

    Here's a simple layout view taken from another answer of mine. Take a look at setContent where the view is swapped with the new one, and remove is called on the old one, if any.

    var Layout = Backbone.View.extend({
        el: 'body' // just for the simple example, let's put this as the body.
    
        // This avoids repeating selector strings everywhere in the view code.
        // If you change a class name in the template, change it only once here.
        regions: {
            header: '.header',
            content: '.content',
            sidebar: '.sidebar'
        },
        initialize: function(options) {
            var regions = this.regions;
    
            // I like to "namespace" my sub-views into an object.
            // That way, you still can access them by name, but you can also
            // loop on the sub-views.
            this.views = {
                sidebar: new SideBar({ el: regions.sidebar }),
                header: new Header({ el: regions.header }),
            };
    
            this.$content = this.$(regions.content);
        },
    
        render: function() {
            _.invoke(this.views, 'render');
            return this;
        },
    
        /**
         * Set the content to a view.
         * @param {Backbone.View} view to replace the content with.
         */
        setContent: function(view) {
            var views = this.views,
                content = views.content;
            if (content !== view) {
                if (content) content.remove();
                views.content = content = view;
                this.$content.html(content.render().el);
            }
        },
    });
    

    Then use it:

    var layout = new Layout(),
        homepage = new HomePage();
    layout.render()
        .setContent(homepage);
    
    // ...later, changing the content view
    layout.setContent(newView); // as simple as this