How to define controller, router and app in the right order using requireJs

Question!

I am writing a small app (initApp.js, initApp.routinj.js, initApp.controller.js) which modules needs to be loaded using requires.
Here's my code (*).
Using console.log in each modules I see that the sequence how the modules are loaded is the following:

1) initController 
2) initRouting 
3) initApp 

Is this the right order?

Now another question.
In initApp.controller.js I need to access the function like initHeader and initSidebar (defined in initApp.js).
But as you can see from my code (initApp.controller.js), console.log('initController', app); returns undefined.
In order to fix this issue I defined the function getApp in initApp.controller.js.
But for sure there is a better way to accomplish this task.
Any idea?
Thanks

(*)


** main.js **

define([
    'js/app',
    'js/init/initApp',
//    'js/tasks/tasksApp'
],
function (App)
{
    "use strict";
    App.initialize();
});

** initApp.js **

/*global define*/
define([
    'backbone',
    'js/app',
    'js/init/initApp.routing',
    'js/init/views/sidebarView',
    'js/init/views/headerView',
],
function (Backbone, App, Router, SidebarView, HeaderView)
{
    "use strict";
    console.log('initApp', Router)
    var initApp = new Backbone.Marionette.Application({

        initHeader: function ()
        {
            var headerView = new HeaderView();
            App.header.show(headerView);
        },

        initSidebar: function ()
        {
            var sidebarView = new SidebarView();
            App.sidebar.show(sidebarView);
        }

    });

    return initApp;
});

** initApp.routin,js **

/*global define*/
define([
    'backbone',
    'marionette',
    'js/init/initApp.controller'
],
function(Backbone, Marionette, controller)
{
    "use strict";
    console.log('initRouting', controller)
    var Router = Backbone.Marionette.AppRouter.extend({

        appRoutes: {
            '*defaults': 'index'
        }

    });
    return new Router({
        controller: controller
    });

});

** initApp.controller.js **

/*global define*/
define([
    'js/init/initApp'
],
function(app)
{
    "use strict";
    console.log('initController', app); // undefined
    var getApp = function () {
        var initApp;
        require(['js/init/initApp'], function (app) {
            initApp = app;
        });
        return initApp;
    };

    var controller = {
        index: function ()
        {
            var app = getApp();
            app.initHeader();
            app.initSidebar();
        }
    }

    return controller;

});


Answers

You have a circular dependency in your files: initApp -> initApp.routing -> initApp.controller -> initApp. This is why you're getting undefined.

At declaration time, when you define your classes/objects, the order only matters if there is a dependency in your code. In my case, I initialize the router and controller in the initApp.js, and so I have:

initApp.js

define(['backbone', 'js/init/initController', 'js/init/initApp.initRouting', ...],
function(Backbone, controller, Router, ...) {
  return {
    initialize: function() {
      // Store a namespaced global reference to my app.
      window.MyApp = new Backbone.Marionette.Application();
      MyApp.addRegions({...});
      MyApp.addInitializers(function(options) {
        MyApp.router =  new Router({controller: controller});
        // Other init stuff...
      });
      MyApp.start();
    }
  };
});

Since I store a reference to my app in window.MyApp, it is now accessible throughout my JS files without needing any additional logic. For instance, I can access the regions directly from the controller or any view:

MyApp.myRegion.show(someView);

And so my main.js is very minimal:

require(['app', 'backbone', 'json2'], function(app){
  window.console = window.console || {log: function() {}}; // Needed for IE.
  app.initialize();
});

Neither of my router or controller JS files depend on each other or on the App file.

initApp.Routing.js

define([
  'jquery',
  'underscore',
  'backbone'
  ], function($, _, Backbone) {
  var Router = Backbone.Marionette.AppRouter.extend({
    appRoutes: {
      // My routes go here...
    }
  });
  return Router;
});

That is, I initialize my router in the App, which reduces dependency between the JS files.

Similarly, my controller has only dependency downwards on various views and collections:

initApp.Routing.js

define([
  'jquery',
  'underscore',
  'backbone',
  'myview',
  'mycollection'
  ], function($, _, Backbone, View, Collection) {
  var controller = {
    showMyView: function() {
      // ...
    }
  });
  return controller;
});

What took me a while to wrap my head around was the difference between declaration and execution. As long as you only declare stuff (i.e., wrap in object, or call extend) in your JS files, I have a single entry-point for execution through your main.js' app.initialize(), you'll be safe and you can access MyApp object anywhere.

Update

For an alternative approach to gaining access to you application instance in other JS files, see the updated Backbone.Marionette wiki on Access Your Application Instance From Other Modules.



This video can help you solving your question :)
By: admin