Created
May 20, 2013 03:43
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); |
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
@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?