0

Our shop just recently adopted Backbone, and even gave me the green light to try out Marionette. However the latter is not giving me the bang for the buck I'd hoped for (possibly due to my ignorance), so instead I'm trying to devise a lightweight dependency injection system where all my Backbone views get an instance of an "Application" object of my own devising.

My system is based on a (necro) answer I posted yesterday and below is code that is working for me. However I don't want to have duplicate blocks of code for each view inside the injectViewsWithApp function, but rather would like to just loop over the views. But this is where I can't figure out how to get things working inside the loop, I think it's some sort of scoping issue.

I will continue working on it so I may post updates to the code during my remaining hour before my weekend begins.

define(['backbone', './app/views/searchform', './app/views/searchresults', './app/views/FooBardetailrow'],

    function(Backbone, SearchFormView, SearchResultsView, FooBarDetailRowView) {
        var APP = initApp();
        injectViewsWithApp();
        launchApp();

        function initApp() {
            return {
                collections : { // No need for separate files/modules if each view gets injected with the APP
                    extendedHaulFooBars : new (Backbone.Collection.extend({})),
                    stn333sts : new (Backbone.Collection.extend({})), 
                    FooBarTypes : new (Backbone.Collection.extend({})),
                    FooBarSymbols : new (Backbone.Collection.extend({})),
                    FooBarSearchParams : new (Backbone.Collection.extend({}))               
                },
                defaultModel : Backbone.Model.extend({}),
                views : {}
            };      
        }

        function injectViewsWithApp() {
            SearchFormView.instantiator = SearchFormView.extend({
                initialize : function() {
                    this.app = APP;
                    SearchFormView.prototype.initialize.apply(this, arguments);
                }
            });

            SearchResultsView.instantiator = SearchResultsView.extend({
                initialize : function() {
                    this.app = APP;
                    SearchResultsView.prototype.initialize.apply(this, arguments);
                }
            });

            FooBarDetailRowView.instantiator = FooBarDetailRowView.extend({
                initialize : function() {
                    this.app = APP;
                    FooBarDetailRowView.prototype.initialize.apply(this, arguments);
                }
            });
        }    
Community
  • 1
  • 1
Dexygen
  • 12,287
  • 13
  • 80
  • 147
  • @mu is too short and/or anybody else that intends to suggest a base view. App is the entry point for, well, the App. How do I get an *initialized* App into BaseView, so extending views can access App? Wouldn't this require that BaseView be extended *at run time*? "Maybe I'm missing something obvious" but isn't it far simpler to inject a dependency at run time? – Dexygen Nov 23 '13 at 03:28

2 Answers2

1

I've simplified things and gotten the following to "work". However, I'd like to assign view.instantiator back to view, and this I cannot get to work :( Definitely will accept somebody else's answer if they can make it so that in the end I can simply call "new MyView()" and get access to APP/app -- otherwise the following is not so bad I suppose.

var APP = {foo : 'bar'};
var MyView = Backbone.View.extend({});
var views = [MyView];

for (var i=views.length; i--; ) {
    var view = views[i];

    view.instantiator = view.extend({
        initialize : function() {
            this.app = APP;
            view.prototype.initialize.apply(this, arguments);
        }
    });

    console.log(new view.instantiator().app.foo);
}
console.log(new MyView.instantiator().app.foo);

// OUTPUT: 'bar' times 2

Dexygen
  • 12,287
  • 13
  • 80
  • 147
0

Here's another arguably better way; inheritance is even possible, as demonstrated, and this is particularly important in my case. Thanks to this fine answer as to how to retain the value of 'app' across calls to the ParentView and ChildView modules.

define(['backbone'],

    function(Backbone) {
        var APP = {
            foo : 'bar'
        };

        var MyParentView = (function() {
            var app;

            return function() {
                return {
                    ctor : Backbone.View.extend({
                        initialize : function() {
                            this.app = app;
                        }
                    }),

                    inject : function(_app) {app = _app;}
                }
            }
        })();

        var MyChildView = (function() {
            var app;

            return function() {
                return {
                    ctor : MyParentView().ctor.extend({
                        initialize : function() {
                            console.log(this.app); // OUTPUT: undefined
                            MyParentView().ctor.prototype.initialize.apply(this, arguments);
                            console.log(this.app.foo); // OUTPUT: bar
                        }
                    }),

                    inject : function(_app) {app = _app;}
                }
            }
        })();           

        var injectableViews = [MyParentView, MyChildView];

        for (var i = injectableViews.length; i--; ) {
            injectableViews[i]().inject(APP);   
        }

        new (MyChildView().ctor)();
    }
);
Community
  • 1
  • 1
Dexygen
  • 12,287
  • 13
  • 80
  • 147