Skip to content

Angular boot script

Andrija Petrovic edited this page Jan 8, 2015 · 1 revision

This story is all about the angular.module function call. Angular providers (services, factories), controllers etc, they all need to reside in modules.

The idea behind mean.io is to encapsulate functionality of each package into a module named mean.package_name, where package_name is, naturally, the name of the corresponding package.

In order to achieve this code architecture in an ordered manner, meanio core searches for the modules (they might be in node_modules; a node module that declares a mean version in its package.json is a mean module; or they might be in the packages directory), and for each module it records a list of its angular dependencies. Each module code should create an instance of meanio.Module, and that module should call the angularDependencies function to declare an array of angular dependencies.

When the modules are found, enlisted and enabled, a data structure enlisting all the modules by their name and their angular dependencies is sent over to the Web browser as a hard-coded JavaScript variable embedded directly into the window.modules variable.

The window.modules variable is an array of {module:package_name, angularDependencies:[...]} hash structures. It is used in the following way:

'use strict';

angular.element(document).ready(function() {
  //Fixing facebook bug with redirect
  if (window.location.hash === '#_=_') window.location.hash = '#!';

  //Then init the app
  angular.bootstrap(document, ['mean']);

});

// Dynamically add angular modules declared by packages
var packageModules = [];
for (var index in window.modules) {
  angular.module(window.modules[index].module, window.modules[index].angularDependencies || []);
  packageModules.push(window.modules[index].module);
}

// Default modules
var modules = ['ngCookies', 'ngResource', 'ui.bootstrap', 'ui.router'];
modules = modules.concat(packageModules);

// Combined modules
angular.module('mean', modules);

The script shown above resides in the packages/system/public/init.js file. I will call the above script "the boot script"

Now, the problem with mean.io lies in the fact that the boot script (like contents of all other files from all other packages) will be aggregated into a minified-or-not file that will be sent over to the browser as a response to the aggregated.js call.

Being aggregated with all the other code, it might easily turn out that the boot script will not be the first code to be ran out of all code from the packages. Some package scripts may be ran before the boot script.

Whatever script runs before the boot script, it better not try to use its angular module. Or it will get Angular errors reporting that a module mean.package_name was not found or was misspelled.

But, when you take a look at how the aggregation code looked like throughout the meanio versions, you will find that never was care taken to somehow put the boot script at the beginning of all the aggregated package code. If you start wondering how the package code works then, you might stumble upon another patch, built into every module (well, almost every, perhaps for historic reasons) by the mean scaffolder.

When a scaffolder build a directory structure for a package named package1, you will get a directory named package1 in which you will have a public subdirectory with a package1.js file that will state

'use strict';

angular.module('mean.package1',[]);

Obviously, scaffolder tries to compensate for the probability that package1 code might be aggregated before the boot script, so that all the package1 code will run without a problem.

However, there is a huge problem: all the code that will declare/define the contents of the mean.package1 will be annulated when the boot script runs. Because boot script will destroy the mean.package1 module that package1 code created and just create an empty new mean.package1 module instead. So, the patch offered by packages/package1/public/package1.js is useless. This is not the way how the synchronicity problem of aggregating the boot script should be approached.

The most reasonable solution might be:

  1. Move the boot script out of the system module into the core.
  2. Install the core's boot script locally through bower by adding a following line into bower.json: "web-bootstrap": "./node_modules/meanio/resources/web-bootstrap.js"
  3. Add the installed bower_component into assets.json by adding "bower_components/web-bootstrap/index.js" into core.js part.
  4. Remove the "angular.module patch" files from all of the packages.
  5. Stop producing the "angular.module patch" file by the scaffolder.

This way the boot script will be treated as "top-level class citizen", and it will surely be ran before any package code.

This problem has already been encountered throughout the mean.io community, and there is already an existing mean issue #963 that addresses just points 4 and 5 of the proposed solution. So, the solution here should be treated as a full-scale solution for #963

Clone this wiki locally