Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Share resources across different amd modules

I'm currently developing a new web application.

This is the first time I'm using requirejs with AMD modules.

It's not that easy to get used to that new paradigm that there are - as I understand it - no variables in the global namespace.

In previous web applications I always had one variable in the global namespace which I could use to share several resources across different modules.

Now with requirejs AMD modules, I use backbone.js and jquery (both amd versions - jquery 1.7.1 and backbone.js 0.5.3-optamd3).

Somewhere in my application I fetch a backbone.js module from the server (user object). I would like to have access to this module from different AMD modules. I also want to have an application wide event object.

Could you tell me: what is the right way in requirejs AMD to share resources across different modules?

like image 227
Wolfgang Adamec Avatar asked Jan 12 '12 21:01

Wolfgang Adamec


3 Answers

I found a solution myself.

Thank you, IntoTheVoid, for your answer, but I was hoping for an AMD-like solution. This means, not again, "polluting" the global namespace.

There were 2 keys to my solution:

"https://github.com/addyosmani/backbone-aura" from Addy Osmani and "https://github.com/amdjs/amdjs-api/wiki/AMD" the The Asynchronous Module Definition (AMD) API specification.

The spec says: "If the factory is a function it should only be executed once."

So, if an amd module is specified multiple times as a dependency in an web application, the dependency is not only NOT LOADED MULTIPLE TIMES, it is also NOT EXECUTED MULTIPLE TIMES, and this is the new thing to me. It is only executed once and the return value of the factory function is kept. Each dependency with the same path has the same object. And this changes everything.

So, you simply define the following amd module:

define([], function() {
    var app_registry = {};
    app_registry.global_event_obj = _.extend({}, Backbone.Events);
    app_registry.models = {};
    return app_registry;
});

Now, in those modules where you want to share resources, you declare this app_registry module as dependency and write in one:

define(['jquery','underscore','backbone','app_registry'], 
  function ($, _, Backbone, app_registry){
    var firstView = Backbone.View.extend({
    initialize: function() {
        _.bindAll (this, 'methodOne'); 
        this.model.bind ('change', this.methodOne);   
        this.model.fetch();
    },  
    methodOne: function() {
        app_registry.models.abc = this.model;
    }
    ...

and in the other:

define(['jquery','underscore','backbone','app_registry'], 
  function ($, _, Backbone, app_registry){
    var secondView = Backbone.View.extend({
    initialize: function() {
       _.bindAll (this, 'methodTwo'); 
        app_registry.global_event_obj.bind ('special', this.methodTwo);
    },  
    methodTwo: function() {
        app_registry. ...
    }
    ...
like image 191
Wolfgang Adamec Avatar answered Nov 27 '22 14:11

Wolfgang Adamec


To answer your question as to how to provide an application wide event object, you can create an amd module called globalContext and instantiate it in the main.js.
Thereafter you can attach settings to the globalContext and use the global context to create subcomponents etc.

    //main.js file
    require([ "./global-context" ], function( GlobalContext) {
             new GlobalContext();
    });

In the global-context.js file we can then perform tasks such as loading child modules

    define(["Boiler", "./settings", "./modules/modules"], 
           function (Boiler, settings, modules) {
                  var GlobalContext = function () {

                //here we use a template javascript class called Boiler.Context
                var globalContext = new Boiler.Context("GlobalModule");

                //add settings to the context which can be obtained globally throughout                    the application
                globalContext.addSettings(settings);

                //here we load the sub modules of the global context
                globalContext.loadChildModules(modules);
};

This is what we have implemented in BoilerplateJS, a reference architecture for large scale javascript product development.

like image 20
Himeshi Avatar answered Nov 27 '22 14:11

Himeshi


You can use the initialize pattern to inject any value into a requirejs module (aka 'dependency injection').

Do a require call anywhere in your code:

var MyBackboneModule;

MyBackboneModule = someWayToGetAReferenceToIt();

require("module", function(Module) { 
  Module.initialize(MyBackboneModule);
});

With module.js file defined as

define([], function(){
   var BackboneModuleDoingThisAndThat;

   function initialize(pBackboneModule) {
      BackboneModuleDoingThisAndThat = pBackboneModule ;
   };

   function doSomething() {
      var x = BackboneModuleDoingThisAndThat.computeThis(42);
   };


   return { 
      initialize: initialize
   };
});
like image 37
widged Avatar answered Nov 27 '22 14:11

widged