AngularJS
is possible, and you can see the code needed for this below.modules
. This variable is used to store a list of all loaded modules, and it is not accessible from the outside.angular.module('moduleName')
at any time to get an existing module. If you output its result to the log, you will notice the property: _invokeQueue
. This is the list of its dependencies.ng-app
directive, if you can find the application module, you can get the entire list of loaded modules and their dependencies. function init(element) { var elements = [element], appElement, module, names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; function append(elm) { return (elm && elements.push(elm)); } angular.forEach(names, function(name) { names[name] = true; append(document.getElementById(name)); name = name.replace(':', '\\:'); if(element.querySelectorAll) { angular.forEach(element.querySelectorAll('.' + name), append); angular.forEach(element.querySelectorAll('.' + name + '\\:'), append); angular.forEach(element.querySelectorAll('[' + name + ']'), append); } }); angular.forEach(elements, function(elm) { if(!appElement) { var className = ' ' + element.className + ' '; var match = NG_APP_CLASS_REGEXP.exec(className); if(match) { appElement = elm; module = (match[2] || '').replace(/\s+/g, ','); } else { angular.forEach(elm.attributes, function(attr) { if(!appElement && names[attr.name]) { appElement = elm; module = attr.value; } }); } } }); if(appElement) { (function addReg(module) { if(regModules.indexOf(module) === -1) { regModules.push(module); var mainModule = angular.module(module); angular.forEach(mainModule.requires, addReg); } })(module); } }
_runBlocks
parameter.$injector
service. function register(providers, registerModules, $log) { var i, ii, k, invokeQueue, moduleName, moduleFn, invokeArgs, provider; if(registerModules) { var runBlocks = []; for(k = registerModules.length - 1; k >= 0; k--) { moduleName = registerModules[k]; regModules.push(moduleName); moduleFn = angular.module(moduleName); runBlocks = runBlocks.concat(moduleFn._runBlocks); try { for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { invokeArgs = invokeQueue[i]; if(providers.hasOwnProperty(invokeArgs[0])) { provider = providers[invokeArgs[0]]; } else { return $log.error("unsupported provider " + invokeArgs[0]); } provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } } catch(e) { if(e.message) { e.message += ' from ' + moduleName; } $log.error(e.message); throw e; } registerModules.pop(); } angular.forEach(runBlocks, function(fn) { providers.$injector.invoke(fn); }); } return null; }
loader([urls], function callback() {});
var modules = {}, asyncLoader, providers = { $controllerProvider: $controllerProvider, $compileProvider: $compileProvider, $filterProvider: $filterProvider, $provide: $provide, // other things $injector: $injector };
this.config = function(config) { if(typeof config.asyncLoader === 'undefined') { throw('You need to define an async loader such as requireJS or script.js'); } asyncLoader = config.asyncLoader; init(angular.element(window.document)); if(typeof config.modules !== 'undefined') { if(angular.isArray(config.modules)) { angular.forEach(config.modules, function(moduleConfig) { modules[moduleConfig.name] = moduleConfig; }); } else { modules[config.modules.name] = config.modules; } } };
angular.module('app').config(['$ocLazyLoadProvider', function($ocLazyLoadProvider) { $ocLazyLoadProvider.config({ modules: [ { name: 'TestModule', files: ['js/testModule.js'], template: 'partials/testLazyLoad.html' } ], asyncLoader: $script }); }]);
$get
property. That that will return this property, and will be available in your service. getModuleConfig: function(name) { if(!modules[name]) { return null; } return modules[name]; }, setModuleConfig: function(module) { modules[module.name] = module; return module; }, getModules: function() { return regModules; }
load: function(name, callback) { var self = this, config, moduleCache = [], deferred = $q.defer(); if(typeof name === 'string') { config = self.getModuleConfig(name); } else if(typeof name === 'object' && typeof name.name !== 'undefined') { config = self.setModuleConfig(name); name = name.name; } moduleCache.push = function(value) { if(this.indexOf(value) === -1) { Array.prototype.push.apply(this, arguments); } }; if(!config) { var errorText = 'Module "' + name + '" not configured'; $log.error(errorText); throw errorText; } }
function getRequires(module) { var requires = []; angular.forEach(module.requires, function(requireModule) { if(regModules.indexOf(requireModule) === -1) { requires.push(requireModule); } }); return requires; }
function moduleExists(moduleName) { try { angular.module(moduleName); } catch(e) { if(/No module/.test(e) || (e.message.indexOf('$injector:nomod') > -1)) { return false; } } return true; }
moduleCache
to get a list of new modules and their dependencies for registration. function loadDependencies(moduleName, allDependencyLoad) { if(regModules.indexOf(moduleName) > -1) { return allDependencyLoad(); } var loadedModule = angular.module(moduleName), requires = getRequires(loadedModule); function onModuleLoad(moduleLoaded) { if(moduleLoaded) { var index = requires.indexOf(moduleLoaded); if(index > -1) { requires.splice(index, 1); } } if(requires.length === 0) { $timeout(function() { allDependencyLoad(moduleName); }); } } var requireNeeded = getRequires(loadedModule); angular.forEach(requireNeeded, function(requireModule) { moduleCache.push(requireModule); if(moduleExists(requireModule)) { return onModuleLoad(requireModule); } var requireModuleConfig = self.getConfig(requireModule); if(requireModuleConfig && (typeof requireModuleConfig.files !== 'undefined')) { asyncLoader(requireModuleConfig.files, function() { loadDependencies(requireModule, function requireModuleLoaded(name) { onModuleLoad(name); }); }); } else { $log.warn('module "' + requireModule + "' not loaded and not configured"); onModuleLoad(requireModule); } return null; }); if(requireNeeded.length === 0) { onModuleLoad(); } return null; }
asyncLoader(config.files, function() { moduleCache.push(name); loadDependencies(name, function() { register(providers, moduleCache, $log); $timeout(function() { deferred.resolve(config); }); }); });
$ocLazyLoad.load({ name: 'TestModule', files: ['js/testModule.js'] }).then(function() { console.log('done!'); });
template
parameter that we mentioned earlier. This template will replace the directive.$templateCache
service to prevent the loading of templates that already exist in the cache of our application. <div oc-lazy-load="{name: 'TestModule', files: ['js/testModule.js'], template: 'partials/testLazyLoad.html'}"></div>
TestModule
module in the provider settings, we can call our directive as follows: <div oc-lazy-load="'TestModule'"></div>
ocLazyLoad.directive('ocLazyLoad', ['$http', '$log', '$ocLazyLoad', '$compile', '$timeout', '$templateCache', function($http, $log, $ocLazyLoad, $compile, $timeout, $templateCache) { return { link: function(scope, element, attr) { var childScope; /** * Destroy the current scope of this element and empty the html */ function clearContent() { if(childScope) { childScope.$destroy(); childScope = null; } element.html(''); } /** * Load a template from cache or url * @param url * @param callback */ function loadTemplate(url, callback) { scope.$apply(function() { var view; if(typeof(view = $templateCache.get(url)) !== 'undefined') { scope.$evalAsync(function() { callback(view); }); } else { $http.get(url) .success(function(data) { $templateCache.put('view:' + url, data); scope.$evalAsync(function() { callback(data); }); }) .error(function(data) { $log.error('Error load template "' + url + "': " + data); }); } }); } scope.$watch(attr.ocLazyLoad, function(moduleName) { if(moduleName) { $ocLazyLoad.load(moduleName).then(function(moduleConfig) { if(!moduleConfig.template) { return; } loadTemplate(moduleConfig.template, function(template) { childScope = scope.$new(); element.html(template); var content = element.contents(); var linkFn = $compile(content); $timeout(function() { linkFn(childScope); }); }); }); } else { clearContent(); } }); } }; }]);
ng-route
).resolve
object or use a template.resolve
object. The resolve
object allows you to define some parameters for your route, and is called before loading the template. This is important, the template can use a controller that performs deferred loading.resolve
function allows the promise to determine how it should be allowed. Since our loading function returns a promise, we can simply use it. Here the views
part is mandatory, this is just for this example. $stateProvider.state('index', { url: "/", // root route views: { "lazyLoadView": { templateUrl: 'partials/testLazyLoad.html' } }, resolve: { test: ['$ocLazyLoad', function($ocLazyLoad) { return $ocLazyLoad.load({ name: 'TestModule', files: ['js/testModule.js'] }); }] } });
$stateProvider.state('index', { url: "/", views: { "lazyLoadView": { template: '<div oc-lazy-load="{name: \'TestModule\', files: [\'js/testModule.js\'], template: \'partials/testLazyLoad.html\'}"></div>' } } });
resolve
, since we added a complex layer, but this can be very useful in some cases.Source: https://habr.com/ru/post/208164/
All Articles