4
votes

I have an application with three pages (which are single-page interfaces). The pages have similar but not identical functionality.

So I want to have javascript-modules that provide the common functionality. Then each page may customize/overwrite parts of the common functionality.

I'm using backbone.js, so what I do is:

  1. load the files containing the common Models, Views, Collections etc.
  2. load app-specific files that customize/overwrite parts of step 1
  3. call an init() function that instanciates all necessary Models/Views/Collections

At the moment I store my modules in a module-container similar to this: https://stackoverflow.com/a/9426071 Each module has an optional init() function that is executed by my main init() function.

Example:

  1. I have a file called results.js. It defines the common Model/Collection/View of search-results. In its init() function everything is instanciated, but this function is not yet called:

    var resultView = new ResultView()
    
  2. Then I include myApp.js, and parts of the View are overwritten.

  3. The main init() function calls results.js init() which instanciates the new View. Everything works nice, smooth and DRY.

Now I want to switch to require.js instead of my own module-container, and wonder how to organize my code.

I could either instanciate all models/views etc. in myApp.js instead of the init() function of each module. This would mean to have a lot of repetitive code.

Or I could stick to each module having its init() function, and call those init() functions in myApp.js. I don't like this because I would have to explicitly write down my modules three times for each of my three pages:

require(['module1', 'module2', 'module3', ...],
  function(module1, module2, module3, ...) {
    var init = function() {
      module1.init();
      module2.init();
      module3.init();
      ...
    }
    return {init: init};
  }
);

For 10 modules plus a number of libraries this is not nice and DRY. Is there any way to access (loop over) all modules that require.js holds?

Or am I missing something, should I structure my code in a different way?

Any hints/thoughts are welcome,

Michael

1
Have you thought about simply looping over arguments in your callback and calling init() if it exists?chrisfrancis27
Which arguments exactly? If you mean this: function(module1, module2, module3, ...) { How can I loop over them without naming them explicitly?Michael
That's exactly it - the arguments object is a built-in property of function, it's an array of all arguments passed to the function. In the case of require.js, you know that the arguments received will exactly match the module definitions you supplied as dependencies.chrisfrancis27
You just improved my knowledge of JavaScript, thanks! arguments seems like a very basic thing, I wonder how I never read about it up to now. This way I can even just expliclitly name the modules once, excellent.Michael
@JohnMunsch Thanks, I'll post it as an answer!chrisfrancis27

1 Answers

4
votes

As discussed in the comments above, you can avoid having to explicitly reference the parameters of the function by looping over them using the arguments object inside the function body. So:

require(['module1', 'module2', 'module3', ..., 'moduleN'],
  function(module1, module2, module3, ..., moduleN) {
    var init = function() {
        module1.init();
        module2.init();
        module3.init();
        ...
        moduleN.init();
    };
    return {init: init};
});

Becomes:

require(['module1', 'module2', 'module3', ..., 'moduleN'],
  function() {
    var args = arguments;
    var init = function() {
        var module;
        for (module in args) {
            if (args.hasOwnProperty(module) && typeof module.init === 'function') {
                module.init.call(this);
            }
        }
    };
    return {init: init};
});

I added in a hasOwnProperty() check inside the for in loop because, for a number of reasons, it's good practice. Also, you'll see the explicit check for init being a function before attempting to call it.

The reason for var args = arguments is so you can reference it from the inner function - otherwise you would be referencing the arguments passed to init(), which is not what you want.

As and aside, on an architectural level, I think the project structure you've described works incredibly well - I use it on a lot of very big projects and it's never let me down. Require.js is awesome! :)