2

I have a backbone app that uses require.js.

Prior to using require my Backbone router looked something like this.

APP.views = {};

APP.Router = Backbone.Router.extend({

routes: {

    '(/)' : 'index',
    'about(/)' : 'about'

},

initialize : function(){
    Backbone.history.start({ pushState: true });
},

index: function() {
    this.showView( new APP.Views.IndexView() );
},

about: function() {
    this.showView( new APP.Views.AboutView() );
},

showView : function( view ) {

        if ( APP.views.current ) {
            APP.views.current.remove();
        }
        APP.views.current = view;
        $( '#page' ).html( view.render().$el );

}
});

I would stash the 'current' view in a global variable and kill the existing view each time a route was changed and life was good.

But, how do I achieve this with require.js ?

My requirejs router currently looks like the following but I'm not sure how to remove the existing views. Although, I have not noticed any of the typical "zombie view" symptoms I feel like I should be removing the existing views.

define( function( require ){

    // DEPS
    var $           = require('jquery'),
        _           = require('underscore'),
        Backbone    = require('backbone');

    // ROUTER
    var Router = Backbone.Router.extend({

        routes: {

            '(/)' : 'index',
            'about(/)' : 'about'

        },
        initialize : function(){

            Backbone.history.start({ pushState: true });

        },
        index: function(){

            this.showPage('index');

        },
        about: function() {

            this.showPage('about');

        },
        showPage : function( pageName ) {

            var view = 'views/pages/' + pageName;

            require( [ view ] , function( Page ) {

                var page = new Page();

                $('#page').html( page.render().el );

            });

        }

    });

    return Router ;

});
master00
  • 141
  • 2
  • 12

1 Answers1

1

Even before using require.js, a global wasn't needed.

Just put the current view into a router property.

initialize : function() {
    this.$page = $('#page');
    Backbone.history.start({ pushState: true });
},
showView : function(view) {
    if (this.current) this.current.remove();
    this.$page.html((this.current = view).render().el);
}

Then, same thing applies to your async require case:

showPage : function(pageName) {
    if (this.current) this.current.remove();

    var view = 'views/pages/' + pageName,
        self = this;
    require([view], function(Page) {
        self.$page.html((self.current = new Page()).render().el);
    });
}

But even then, I don't feel like requiring each view with an async require is worth it. You're just slowing down your application with a lot of extra requests.

Just define the dependencies for each module.

define([
    'jquery',
    'backbone',
    'views/index', 
    'views/about'
], function($, Backbone, IndexView, AboutView){
    // ...
});

While in development, you'll see a lot of request each time you refresh, but when ready for production, build a minified bundle of all the js files with require optimizer.


Also note that you can have module scope global, which are just local variable declared at the root of a module scope (IIFE or with require.js).

(function() {
    var currentView;

    var Router = Backbone.Router.extend({
        // ...snip...
        showView: function(view) {
            if (currentView) currentView.remove();
            this.$page.html((currentView = view).render().el);
        }
    });
})();
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • Thanks! Not sure why I never thought about that. The only reason I'm using the async require is because my view has a dependency on the router and I was trying to avoid circular dependencies. Unless, there is a better way to handle that situation? – master00 Dec 06 '17 at 23:03
  • @master00 your views shouldn't have a dependency on the router, but you're right in that an async require like that is the only solution to avoid circular dependencies (without a refactor). – Emile Bergeron Dec 06 '17 at 23:39
  • OK, got it. So, is there never a practical situation to call router.navigate() from within a view? – master00 Dec 06 '17 at 23:48
  • @master00 See [Difference between `Backbone.history.navigate` and `this.router.navigate`](https://stackoverflow.com/a/43895315/1218980) – Emile Bergeron Dec 06 '17 at 23:53
  • @master00 I have a [list of answers to common Backbone problems on my profile](https://stackoverflow.com/users/1218980/emile-bergeron?tab=profile) that might help you. – Emile Bergeron Dec 06 '17 at 23:56
  • 1
    Great, thanks. I'm still trying to wrap my head around all the concepts. Almost threw in the towel many times now but you've helped me understand a few things that were giving me a headache. By the time I figure out everything I'm sure there will already be some new technologies I'll have to learn all over again ;) – master00 Dec 07 '17 at 00:05
  • @master00 it's funny you say that cause Backbone is pretty much dead already. Though the concepts within Backbone are still good, stuff like the new Angular and Vue.js are light years ahead! – Emile Bergeron Dec 07 '17 at 00:10
  • 1
    Ya, I've come to realize that. I was researching Vue but I'm already waste deep in Backbone. At this point, as long as I have ANY scalable architecture to complete my project I'm happy to settle for that. – master00 Dec 07 '17 at 00:13