4

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

Community
  • 1
  • 1
Michael
  • 731
  • 1
  • 7
  • 11
  • 1
    Have you thought about simply looping over `arguments` in your callback and calling `init()` if it exists? – chrisfrancis27 Jul 31 '12 at 12:59
  • Which `arguments` exactly? If you mean this: `function(module1, module2, module3, ...) {` How can I loop over them without naming them explicitly? – Michael Jul 31 '12 at 13:14
  • 1
    That's exactly it - the [`arguments` object](https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments) 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 Jul 31 '12 at 13:18
  • 1
    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 Jul 31 '12 at 13:27
  • @ChrisFrancis It would be better for SO if you could write that as an answer on the question and Michael could then vote it and accept it as the best answer. Then it will appear as an answered question in searches. – John Munsch Jul 31 '12 at 16:10
  • 1
    @JohnMunsch Thanks, I'll post it as an answer! – chrisfrancis27 Jul 31 '12 at 16:12

1 Answers1

4

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! :)

chrisfrancis27
  • 4,516
  • 1
  • 24
  • 32