Skip to content

Instantly share code, notes, and snippets.

@nateabele
Created May 20, 2013 03:43
Show Gist options
  • Save nateabele/5610305 to your computer and use it in GitHub Desktop.
Save nateabele/5610305 to your computer and use it in GitHub Desktop.
A simple, naïve, and hacky proof-of-concept for auto-loading modules in AngularJS.
/**
* Naive AngularJS module autoloader; depends on require.js.
*
* Usage:
*
* angular.autoLoad({
* 'ngResource': '/js/angular-resource-1.0.1.min.js',
* 'google-maps': '/js/angular-google-maps.js',
* 'ui.bootstrap': '/js/ui/ui-bootstrap-custom-tpls-0.4.0.min.js',
* 'module.split.over.multiple.files': ['/js/file1.js', '/js/file2.js']
* });
*
* Then initialize your modules with their dependencies as normal, and the correct scripts will be
* autoloaded appropriately.
*
* Notes:
* As mentioned, this is currently implemented with required.js. It should be rewritten using $q.
* ALSO, Angular must be boostrapped manually (http://docs.angularjs.org/guide/bootstrap) *after*
* your autoload configuration is initialized, and *before* your application modules are loaded.
*/
(function(angular) {
var _module = angular.module, _bootstrap = angular.bootstrap, _requests = 0, _inited = false;
var _loadMap = {}, _invokeQueue = {}, _loaded = {}, _funcCache = {}, _ = {};
var _appendAndFlatten = function(list, item) {
if (!angular.isArray(item)) {
if (list.indexOf(item) === -1) {
list.push(item);
}
return list;
}
angular.forEach(item, function(i) {
if (list.indexOf(i) === -1) {
list.push(i);
}
});
return list;
};
angular.extend(angular, {
module: function(name, requires, configFn) {
if (!requires) {
return _module.call(angular, name, requires, configFn);
}
var autoLoad = [];
angular.forEach(requires, function(mod) {
try {
angular.module(mod);
} catch (e) {
if (!e.toString().match(/No module/)) {
throw e;
}
if (_loadMap[mod]) {
autoLoad = _appendAndFlatten(autoLoad, _loadMap[mod]);
}
}
});
if (!autoLoad.length || (autoLoad.length === 1 && autoLoad[0] === "ng")) {
_inited = true;
return _module.call(angular, name, requires, configFn);
}
var module = _module.call(angular, name, [], configFn);
angular.forEach(module, function(val, key) {
if (!angular.isFunction(val)) {
return;
}
module[key] = function() {
_invokeQueue[name].push([key, Array.prototype.slice.call(arguments)]);
return module;
};
_funcCache[key] = val;
});
_invokeQueue[name] = [];
require(autoLoad, function() {
for (var n in _funcCache) {
module[n] = _funcCache[n];
}
module.requires = requires;
if (angular.isFunction(configFn)) {
module.config(configFn);
}
for (var i = 0; i < _invokeQueue[name].length; i++) {
var call = _invokeQueue[name][i];
module[call[0]].apply(module, call[1]);
}
_requests--;
if (_.businessTime) {
_.businessTime();
}
});
_requests++;
_inited = true;
return module;
},
autoLoad: function(modules) {
angular.extend(_loadMap, modules);
},
bootstrap: function() {
var args = Array.prototype.slice.call(arguments);
_.businessTime = function() {
if (_inited && _requests === 0) {
return _bootstrap.apply(angular, args);
}
};
_.businessTime();
}
});
})(angular);
@laurelnaiad
Copy link

@nateabele -- I put some comments in a fork... are they mostly accurate? https://gist.github.com/stu-salsbury/5691371

I'm going to try to wrangle this a little bit, I'll keep you posted.

@laurelnaiad
Copy link

@nateabele: so far no luck getting it to go at runtime -- it might make more sense for my purposes to try to take the logic of separately invoking the functions and put it into something that takes a module object as a parameter so that the top level app isn't passed by name...basically a wrapper around your module function that directly populates an existing module?

@laurelnaiad
Copy link

Hmm. I think I see a serious roadblock -- anything written to use injected providers isn't going to work at runtime. That may be a deal killer...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment